From 53d17fc40e9bd5aab1c052fb3d8a1631b3f9af97 Mon Sep 17 00:00:00 2001 From: Andrew Wilkinson Date: Tue, 6 Jun 2017 16:21:14 +0100 Subject: [PATCH] Read baseline TIFF metadata --- src/ImageSharp/Formats/Tiff/README.md | 16 ++-- .../Formats/Tiff/TiffDecoderCore.cs | 49 ++++++++++++ .../Formats/Tiff/TiffMetadataNames.cs | 53 +++++++++++++ .../Formats/Tiff/TiffDecoderMetadataTests.cs | 74 ++++++++++++++++++- 4 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index d668ed449..c2527b008 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -79,9 +79,9 @@ |CellWidth | | | | |CellLength | | | | |FillOrder | | | | -|ImageDescription | | | | -|Make | | | | -|Model | | | | +|ImageDescription | | Y | | +|Make | | Y | | +|Model | | Y | | |StripOffsets | | Y | | |Orientation | | | | |SamplesPerPixel | | | Currently ignored, as can be inferred from count of BitsPerSample | @@ -97,13 +97,13 @@ |GrayResponseUnit | | | | |GrayResponseCurve | | | | |ResolutionUnit | | Y | | -|Software | | | | -|DateTime | | | | -|Artist | | | | -|HostComputer | | | | +|Software | | Y | | +|DateTime | | Y | | +|Artist | | Y | | +|HostComputer | | Y | | |ColorMap | | Y | | |ExtraSamples | | | | -|Copyright | | | | +|Copyright | | Y | | ### Extension TIFF Tags diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 806d56334..d2446bb76 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -224,6 +224,12 @@ namespace ImageSharp.Formats return image; } + /// + /// Reads the image metadata from a specified IFD. + /// + /// The pixel format. + /// The IFD to read the image from. + /// The image to write the metadata to. public void ReadMetadata(TiffIfd ifd, Image image) where TPixel : struct, IPixel { @@ -245,6 +251,49 @@ namespace ImageSharp.Formats image.MetaData.VerticalResolution = yResolution.ToDouble() * resolutionUnitFactor; } } + + if (!this.options.IgnoreMetadata) + { + if (ifd.TryGetIfdEntry(TiffTags.Artist, out TiffIfdEntry artistEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Artist, this.ReadString(ref artistEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Copyright, out TiffIfdEntry copyrightEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Copyright, this.ReadString(ref copyrightEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.DateTime, out TiffIfdEntry dateTimeEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.DateTime, this.ReadString(ref dateTimeEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.HostComputer, out TiffIfdEntry hostComputerEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.HostComputer, this.ReadString(ref hostComputerEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.ImageDescription, out TiffIfdEntry imageDescriptionEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.ImageDescription, this.ReadString(ref imageDescriptionEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Make, out TiffIfdEntry makeEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Make, this.ReadString(ref makeEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Model, out TiffIfdEntry modelEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Model, this.ReadString(ref modelEntry))); + } + + if (ifd.TryGetIfdEntry(TiffTags.Software, out TiffIfdEntry softwareEntry)) + { + image.MetaData.Properties.Add(new ImageProperty(TiffMetadataNames.Software, this.ReadString(ref softwareEntry))); + } + } } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs new file mode 100644 index 000000000..4591986b0 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/TiffMetadataNames.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Defines constants for each of the supported TIFF metadata types. + /// + public static class TiffMetadataNames + { + /// + /// Person who created the image. + /// + public const string Artist = "Artist"; + + /// + /// Copyright notice. + /// + public const string Copyright = "Copyright"; + + /// + /// Date and time of image creation. + /// + public const string DateTime = "DateTime"; + + /// + /// The computer and/or operating system in use at the time of image creation. + /// + public const string HostComputer = "HostComputer"; + + /// + /// A string that describes the subject of the image. + /// + public const string ImageDescription = "ImageDescription"; + + /// + /// The scanner/camera manufacturer. + /// + public const string Make = "Make"; + + /// + /// The scanner/camera model name or number. + /// + public const string Model = "Model"; + + /// + /// Name and version number of the software package(s) used to create the image. + /// + public const string Software = "Software"; + } +} diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs index b3dd30f5e..e418d0d67 100644 --- a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs +++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderMetadataTests.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Tests { using System; using System.IO; + using System.Linq; using Xunit; using ImageSharp.Formats; @@ -14,8 +15,22 @@ namespace ImageSharp.Tests public class TiffDecoderMetadataTests { - public static object[][] IsLittleEndianValues = new[] { new object[] { false }, - new object[] { true } }; + public static object[][] BaselineMetadataValues = new[] { new object[] { false, TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, + new object[] { false, TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" }, + new object[] { false, TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" }, + new object[] { false, TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" }, + new object[] { false, TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" }, + new object[] { false, TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" }, + new object[] { false, TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" }, + new object[] { false, TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }, + new object[] { true, TiffTags.Artist, TiffMetadataNames.Artist, "My Artist Name" }, + new object[] { true, TiffTags.Copyright, TiffMetadataNames.Copyright, "My Copyright Statement" }, + new object[] { true, TiffTags.DateTime, TiffMetadataNames.DateTime, "My DateTime Value" }, + new object[] { true, TiffTags.HostComputer, TiffMetadataNames.HostComputer, "My Host Computer Name" }, + new object[] { true, TiffTags.ImageDescription, TiffMetadataNames.ImageDescription, "My Image Description" }, + new object[] { true, TiffTags.Make, TiffMetadataNames.Make, "My Camera Make" }, + new object[] { true, TiffTags.Model, TiffMetadataNames.Model, "My Camera Model" }, + new object[] { true, TiffTags.Software, TiffMetadataNames.Software, "My Imaging Software" }}; [Theory] [InlineData(false, 150u, 1u, 200u, 1u, 2u /* Inch */, 150.0, 200.0)] @@ -34,7 +49,7 @@ namespace ImageSharp.Tests [InlineData(true, null, null, null, null, null /* Inch */, 96.0, 96.0)] [InlineData(true, 150u, 1u, null, null, 2u /* Inch */, 150.0, 96.0)] [InlineData(true, null, null, 200u, 1u, 2u /* Inch */, 96.0, 200.0)] - public void DecodeImage_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, + public void ReadMetadata_SetsImageResolution(bool isLittleEndian, uint? xResolutionNumerator, uint? xResolutionDenominator, uint? yResolutionNumerator, uint? yResolutionDenominator, uint? resolutionUnit, double expectedHorizonalResolution, double expectedVerticalResolution) { @@ -66,5 +81,58 @@ namespace ImageSharp.Tests Assert.Equal(expectedHorizonalResolution, image.MetaData.HorizontalResolution, 10); Assert.Equal(expectedVerticalResolution, image.MetaData.VerticalResolution, 10); } + + [Theory] + [MemberData(nameof(BaselineMetadataValues))] + public void ReadMetadata_SetsAsciiMetadata(bool isLittleEndian, ushort tag, string metadataName, string metadataValue) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Ascii(tag, metadataValue), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) + } + } + .ToStream(isLittleEndian); + + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, null, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(null, 20, 20); + + decoder.ReadMetadata(ifd, image); + var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName)?.Value; + + Assert.Equal(metadataValue, metadata); + } + + [Theory] + [MemberData(nameof(BaselineMetadataValues))] + public void ReadMetadata_DoesntSetMetadataIfIgnoring(bool isLittleEndian, ushort tag, string metadataName, string metadataValue) + { + Stream stream = new TiffGenIfd() + { + Entries = + { + TiffGenEntry.Integer(TiffTags.ImageWidth, TiffType.Long, 150), + TiffGenEntry.Integer(TiffTags.ImageLength, TiffType.Long, 210), + TiffGenEntry.Ascii(tag, metadataValue), + TiffGenEntry.Integer(TiffTags.Orientation, TiffType.Short, 1) + } + } + .ToStream(isLittleEndian); + + DecoderOptions options = new DecoderOptions() { IgnoreMetadata = true }; + TiffDecoderCore decoder = new TiffDecoderCore(stream, isLittleEndian, options, null); + TiffIfd ifd = decoder.ReadIfd(0); + Image image = new Image(null, 20, 20); + + decoder.ReadMetadata(ifd, image); + var metadata = image.MetaData.Properties.FirstOrDefault(m => m.Name == metadataName)?.Value; + + Assert.Equal(null, metadata); + } } } \ No newline at end of file