diff --git a/MetadataExtractor/ImageMetadataReader.cs b/MetadataExtractor/ImageMetadataReader.cs index c2995fae3..40ee0cff7 100644 --- a/MetadataExtractor/ImageMetadataReader.cs +++ b/MetadataExtractor/ImageMetadataReader.cs @@ -57,12 +57,13 @@ public static class ImageMetadataReader { /// Reads metadata from an . /// A stream from which the file data may be read. The stream must be positioned at the beginning of the file's data. + /// The file name, if available. May contain path information if that's more convenient. The method will only inspect the extension of the path (i.e. EndsWith). /// A list of instances containing the various types of metadata found within the file's data. /// The file type is unknown, or processing errors occurred. /// - public static IReadOnlyList ReadMetadata(Stream stream) + public static IReadOnlyList ReadMetadata(Stream stream, string? fileName = null) { - var fileType = FileTypeDetector.DetectFileType(stream); + var fileType = FileTypeDetector.DetectFileType(stream, fileName); var directories = new List(); @@ -76,23 +77,29 @@ public static IReadOnlyList ReadMetadata(Stream stream) FileType.Bmp => BmpMetadataReader.ReadMetadata(stream), FileType.Crx => QuickTimeMetadataReader.ReadMetadata(stream), FileType.Cr2 => TiffMetadataReader.ReadMetadata(stream), + FileType.Dng => TiffMetadataReader.ReadMetadata(stream), FileType.Eps => EpsMetadataReader.ReadMetadata(stream), FileType.Gif => GifMetadataReader.ReadMetadata(stream), + FileType.GoPro => TiffMetadataReader.ReadMetadata(stream), FileType.Heif => HeifMetadataReader.ReadMetadata(stream), FileType.Ico => IcoMetadataReader.ReadMetadata(stream), FileType.Jpeg => JpegMetadataReader.ReadMetadata(stream), + FileType.Kdc => TiffMetadataReader.ReadMetadata(stream), FileType.Mp3 => Mp3MetadataReader.ReadMetadata(stream), FileType.Mp4 => QuickTimeMetadataReader.ReadMetadata(stream), FileType.Nef => TiffMetadataReader.ReadMetadata(stream), FileType.Netpbm => [NetpbmMetadataReader.ReadMetadata(stream)], FileType.Orf => TiffMetadataReader.ReadMetadata(stream), + FileType.Pef => TiffMetadataReader.ReadMetadata(stream), FileType.Pcx => [PcxMetadataReader.ReadMetadata(stream)], FileType.Png => PngMetadataReader.ReadMetadata(stream), FileType.Psd => PsdMetadataReader.ReadMetadata(stream), FileType.QuickTime => QuickTimeMetadataReader.ReadMetadata(stream), FileType.Raf => RafMetadataReader.ReadMetadata(stream), FileType.Rw2 => TiffMetadataReader.ReadMetadata(stream), + FileType.Srw => TiffMetadataReader.ReadMetadata(stream), FileType.Tga => TgaMetadataReader.ReadMetadata(stream), + FileType.ThreeFR => TiffMetadataReader.ReadMetadata(stream), FileType.Tiff => TiffMetadataReader.ReadMetadata(stream), FileType.Wav => WavMetadataReader.ReadMetadata(stream), FileType.WebP => WebPMetadataReader.ReadMetadata(stream), @@ -109,7 +116,7 @@ public static IReadOnlyList ReadMetadata(Stream stream) } /// Reads metadata from a file. - /// Unlike , this overload includes a in the output. + /// Unlike , this overload includes a in the output. /// Location of a file from which data should be read. /// A list of instances containing the various types of metadata found within the file's data. /// The file type is unknown, or processing errors occurred. @@ -119,7 +126,7 @@ public static IReadOnlyList ReadMetadata(string filePath) var directories = new List(); using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) - directories.AddRange(ReadMetadata(stream)); + directories.AddRange(ReadMetadata(stream, filePath)); directories.Add(new FileMetadataReader().Read(filePath)); diff --git a/MetadataExtractor/PublicAPI/net462/PublicAPI.Shipped.txt b/MetadataExtractor/PublicAPI/net462/PublicAPI.Shipped.txt index 5ef4916ef..5c0e5ea5e 100644 --- a/MetadataExtractor/PublicAPI/net462/PublicAPI.Shipped.txt +++ b/MetadataExtractor/PublicAPI/net462/PublicAPI.Shipped.txt @@ -4378,13 +4378,13 @@ static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSeconds(double value static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSecondsString(double value) -> string! static MetadataExtractor.GeoLocation.DegreesMinutesSecondsToDecimal(MetadataExtractor.Rational degs, MetadataExtractor.Rational mins, MetadataExtractor.Rational secs, bool isNegative) -> double? static MetadataExtractor.ImageMetadataReader.ReadMetadata(string! filePath) -> System.Collections.Generic.IReadOnlyList! -static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream) -> System.Collections.Generic.IReadOnlyList! +static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream, string? fileName = null) -> System.Collections.Generic.IReadOnlyList! static MetadataExtractor.Rational.operator !=(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.Rational.operator ==(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.TagDescriptor.ConvertBytesToVersionString(int[]? components, int majorDigits) -> string? static MetadataExtractor.TagDescriptor.GetFocalLengthDescription(double mm) -> string! static MetadataExtractor.TagDescriptor.GetFStopDescription(double fStop) -> string! -static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream) -> MetadataExtractor.Util.FileType +static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream, string? fileName = null) -> MetadataExtractor.Util.FileType static MetadataExtractor.Util.FileTypeExtensions.GetAllExtensions(this MetadataExtractor.Util.FileType fileType) -> System.Collections.Generic.IEnumerable? static MetadataExtractor.Util.FileTypeExtensions.GetCommonExtension(this MetadataExtractor.Util.FileType fileType) -> string? static MetadataExtractor.Util.FileTypeExtensions.GetLongName(this MetadataExtractor.Util.FileType fileType) -> string! diff --git a/MetadataExtractor/PublicAPI/net462/PublicAPI.Unshipped.txt b/MetadataExtractor/PublicAPI/net462/PublicAPI.Unshipped.txt index dbb92d0cb..4ae22099b 100644 --- a/MetadataExtractor/PublicAPI/net462/PublicAPI.Unshipped.txt +++ b/MetadataExtractor/PublicAPI/net462/PublicAPI.Unshipped.txt @@ -115,6 +115,12 @@ MetadataExtractor.IO.IndexedCapturingReader.Dispose() -> void MetadataExtractor.IO.IndexedReader.GetByte(int index) -> byte MetadataExtractor.IO.IndexedReader.GetBytes(int index, int count) -> byte[]! MetadataExtractor.Util.FileType.Avif = 28 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Dng = 29 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.GoPro = 30 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Kdc = 31 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.ThreeFR = 32 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Pef = 33 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Srw = 34 -> MetadataExtractor.Util.FileType override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDescriptor.GetDescription(int tagType) -> string? override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDirectory.Name.get -> string! override MetadataExtractor.Formats.Exif.Makernotes.NikonPictureControl1Descriptor.GetDescription(int tagType) -> string? diff --git a/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Shipped.txt b/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Shipped.txt index 4bd137ed8..21349b45b 100644 --- a/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -4366,13 +4366,13 @@ static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSeconds(double value static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSecondsString(double value) -> string! static MetadataExtractor.GeoLocation.DegreesMinutesSecondsToDecimal(MetadataExtractor.Rational degs, MetadataExtractor.Rational mins, MetadataExtractor.Rational secs, bool isNegative) -> double? static MetadataExtractor.ImageMetadataReader.ReadMetadata(string! filePath) -> System.Collections.Generic.IReadOnlyList! -static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream) -> System.Collections.Generic.IReadOnlyList! +static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream, string? fileName = null) -> System.Collections.Generic.IReadOnlyList! static MetadataExtractor.Rational.operator !=(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.Rational.operator ==(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.TagDescriptor.ConvertBytesToVersionString(int[]? components, int majorDigits) -> string? static MetadataExtractor.TagDescriptor.GetFocalLengthDescription(double mm) -> string! static MetadataExtractor.TagDescriptor.GetFStopDescription(double fStop) -> string! -static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream) -> MetadataExtractor.Util.FileType +static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream, string? fileName = null) -> MetadataExtractor.Util.FileType static MetadataExtractor.Util.FileTypeExtensions.GetAllExtensions(this MetadataExtractor.Util.FileType fileType) -> System.Collections.Generic.IEnumerable? static MetadataExtractor.Util.FileTypeExtensions.GetCommonExtension(this MetadataExtractor.Util.FileType fileType) -> string? static MetadataExtractor.Util.FileTypeExtensions.GetLongName(this MetadataExtractor.Util.FileType fileType) -> string! diff --git a/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 2cc02a266..ce84c159f 100644 --- a/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/MetadataExtractor/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -114,6 +114,12 @@ MetadataExtractor.IO.IndexedCapturingReader.Dispose() -> void MetadataExtractor.IO.IndexedReader.GetByte(int index) -> byte MetadataExtractor.IO.IndexedReader.GetBytes(int index, int count) -> byte[]! MetadataExtractor.Util.FileType.Avif = 28 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Dng = 29 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.GoPro = 30 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Kdc = 31 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.ThreeFR = 32 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Pef = 33 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Srw = 34 -> MetadataExtractor.Util.FileType override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDescriptor.GetDescription(int tagType) -> string? override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDirectory.Name.get -> string! override MetadataExtractor.Formats.Exif.Makernotes.NikonPictureControl1Descriptor.GetDescription(int tagType) -> string? diff --git a/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Shipped.txt b/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Shipped.txt index ce0feab30..8885de685 100644 --- a/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Shipped.txt +++ b/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Shipped.txt @@ -4371,13 +4371,13 @@ static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSeconds(double value static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSecondsString(double value) -> string! static MetadataExtractor.GeoLocation.DegreesMinutesSecondsToDecimal(MetadataExtractor.Rational degs, MetadataExtractor.Rational mins, MetadataExtractor.Rational secs, bool isNegative) -> double? static MetadataExtractor.ImageMetadataReader.ReadMetadata(string! filePath) -> System.Collections.Generic.IReadOnlyList! -static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream) -> System.Collections.Generic.IReadOnlyList! +static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream, string? fileName = null) -> System.Collections.Generic.IReadOnlyList! static MetadataExtractor.Rational.operator !=(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.Rational.operator ==(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.TagDescriptor.ConvertBytesToVersionString(int[]? components, int majorDigits) -> string? static MetadataExtractor.TagDescriptor.GetFocalLengthDescription(double mm) -> string! static MetadataExtractor.TagDescriptor.GetFStopDescription(double fStop) -> string! -static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream) -> MetadataExtractor.Util.FileType +static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream, string? fileName = null) -> MetadataExtractor.Util.FileType static MetadataExtractor.Util.FileTypeExtensions.GetAllExtensions(this MetadataExtractor.Util.FileType fileType) -> System.Collections.Generic.IEnumerable? static MetadataExtractor.Util.FileTypeExtensions.GetCommonExtension(this MetadataExtractor.Util.FileType fileType) -> string? static MetadataExtractor.Util.FileTypeExtensions.GetLongName(this MetadataExtractor.Util.FileType fileType) -> string! diff --git a/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Unshipped.txt b/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Unshipped.txt index dbb92d0cb..4ae22099b 100644 --- a/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Unshipped.txt +++ b/MetadataExtractor/PublicAPI/netstandard1.3/PublicAPI.Unshipped.txt @@ -115,6 +115,12 @@ MetadataExtractor.IO.IndexedCapturingReader.Dispose() -> void MetadataExtractor.IO.IndexedReader.GetByte(int index) -> byte MetadataExtractor.IO.IndexedReader.GetBytes(int index, int count) -> byte[]! MetadataExtractor.Util.FileType.Avif = 28 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Dng = 29 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.GoPro = 30 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Kdc = 31 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.ThreeFR = 32 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Pef = 33 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Srw = 34 -> MetadataExtractor.Util.FileType override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDescriptor.GetDescription(int tagType) -> string? override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDirectory.Name.get -> string! override MetadataExtractor.Formats.Exif.Makernotes.NikonPictureControl1Descriptor.GetDescription(int tagType) -> string? diff --git a/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt b/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt index ca94a00fa..3b8af8019 100644 --- a/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt +++ b/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt @@ -4373,13 +4373,13 @@ static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSeconds(double value static MetadataExtractor.GeoLocation.DecimalToDegreesMinutesSecondsString(double value) -> string! static MetadataExtractor.GeoLocation.DegreesMinutesSecondsToDecimal(MetadataExtractor.Rational degs, MetadataExtractor.Rational mins, MetadataExtractor.Rational secs, bool isNegative) -> double? static MetadataExtractor.ImageMetadataReader.ReadMetadata(string! filePath) -> System.Collections.Generic.IReadOnlyList! -static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream) -> System.Collections.Generic.IReadOnlyList! +static MetadataExtractor.ImageMetadataReader.ReadMetadata(System.IO.Stream! stream, string? fileName = null) -> System.Collections.Generic.IReadOnlyList! static MetadataExtractor.Rational.operator !=(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.Rational.operator ==(MetadataExtractor.Rational a, MetadataExtractor.Rational b) -> bool static MetadataExtractor.TagDescriptor.ConvertBytesToVersionString(int[]? components, int majorDigits) -> string? static MetadataExtractor.TagDescriptor.GetFocalLengthDescription(double mm) -> string! static MetadataExtractor.TagDescriptor.GetFStopDescription(double fStop) -> string! -static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream) -> MetadataExtractor.Util.FileType +static MetadataExtractor.Util.FileTypeDetector.DetectFileType(System.IO.Stream! stream, string? fileName = null) -> MetadataExtractor.Util.FileType static MetadataExtractor.Util.FileTypeExtensions.GetAllExtensions(this MetadataExtractor.Util.FileType fileType) -> System.Collections.Generic.IEnumerable? static MetadataExtractor.Util.FileTypeExtensions.GetCommonExtension(this MetadataExtractor.Util.FileType fileType) -> string? static MetadataExtractor.Util.FileTypeExtensions.GetLongName(this MetadataExtractor.Util.FileType fileType) -> string! diff --git a/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt b/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt index 2cc02a266..ce84c159f 100644 --- a/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt +++ b/MetadataExtractor/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt @@ -114,6 +114,12 @@ MetadataExtractor.IO.IndexedCapturingReader.Dispose() -> void MetadataExtractor.IO.IndexedReader.GetByte(int index) -> byte MetadataExtractor.IO.IndexedReader.GetBytes(int index, int count) -> byte[]! MetadataExtractor.Util.FileType.Avif = 28 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Dng = 29 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.GoPro = 30 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Kdc = 31 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.ThreeFR = 32 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Pef = 33 -> MetadataExtractor.Util.FileType +MetadataExtractor.Util.FileType.Srw = 34 -> MetadataExtractor.Util.FileType override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDescriptor.GetDescription(int tagType) -> string? override MetadataExtractor.Formats.Exif.Makernotes.AppleRunTimeMakernoteDirectory.Name.get -> string! override MetadataExtractor.Formats.Exif.Makernotes.NikonPictureControl1Descriptor.GetDescription(int tagType) -> string? diff --git a/MetadataExtractor/Util/FileType.cs b/MetadataExtractor/Util/FileType.cs index c9d58a768..78bf5d850 100644 --- a/MetadataExtractor/Util/FileType.cs +++ b/MetadataExtractor/Util/FileType.cs @@ -78,7 +78,7 @@ public enum FileType /// Encapsulated PostScript. Eps = 23, - /// Truevision graphics. + /// Truevision graphics (Targa). Tga = 24, /// MPEG-1 / MPEG-2 Audio Layer III. @@ -91,7 +91,25 @@ public enum FileType Mp4 = 27, /// AV1 Image File Format. - Avif = 28 + Avif = 28, + + /// DNG Image File Format. + Dng = 29, + + /// GPR (GoPro) Image File Format. + GoPro = 30, + + /// KDC (Kodak) Image File Format. + Kdc = 31, + + /// 3FR (Hasselblad) Image File Format. + ThreeFR = 32, + + /// PEF (Pentax) Image File Format. + Pef = 33, + + /// SRW (Samsung) Image File Format. + Srw = 34, } public static class FileTypeExtensions @@ -126,7 +144,13 @@ public static class FileTypeExtensions "MP3", "HEIF", "MP4", - "AVIF" + "AVIF", + "DNG", + "GPR", + "KDC", + "3FR", + "PEF", + "SRW", ]; private static readonly string[] _longNames = @@ -160,6 +184,12 @@ public static class FileTypeExtensions "High Efficiency Image File Format", "MPEG-4 Part 14", "AV1 Image File Format", + "Digital Negative", + "GoPro Raw", + "Kodak Raw", + "Hasselblad Raw", + "Pentax Raw", + "Samsung Raw", ]; private static readonly string?[] _mimeTypes = @@ -177,12 +207,12 @@ public static class FileTypeExtensions "audio/vnd.wave", "video/vnd.avi", "image/webp", - null, // Sony RAW - null, - null, - null, - null, - null, + "image/x-sony-arw", // https://stackoverflow.com/a/47612661/24874 + "image/x-canon-crw", + "image/x-canon-cr2", + "image/x-nikon-nef", + "image/x-olympus-orf", + "image/x-fuji-raf", null, "video/quicktime", "image/x-portable-graymap", @@ -192,7 +222,13 @@ public static class FileTypeExtensions "audio/mpeg", "image/heic", "video/mp4", - "image/avif" + "image/avif", + "image/x-adobe-dng", + "image/x-gopro-gpr", + "image/x-kodak-kdc", + "image/x-hasselblad-3fr", + "image/x-pentax-pef", + "image/x-samsung-srw", ]; private static readonly string[]?[] _extensions = @@ -225,7 +261,13 @@ public static class FileTypeExtensions ["mp3"], ["heic", "heif", "avci"], ["mp4", "m4a", "m4p", "m4b", "m4r", "m4v"], - ["avif"] + ["avif"], + ["dng"], + ["gpr"], + ["kdc"], + ["3fr"], + ["pef"], + ["srw"], ]; public static string GetName(this FileType fileType) diff --git a/MetadataExtractor/Util/FileTypeDetector.cs b/MetadataExtractor/Util/FileTypeDetector.cs index fc76042e1..224145b03 100644 --- a/MetadataExtractor/Util/FileTypeDetector.cs +++ b/MetadataExtractor/Util/FileTypeDetector.cs @@ -47,7 +47,8 @@ public static class FileTypeDetector { FileType.Pcx, [0x0A, 0x05, 0x01] }, { FileType.Eps, "%!PS"u8 }, { FileType.Eps, [0xC5, 0xD0, 0xD3, 0xC6] }, - { FileType.Arw, "II"u8.ToArray(), [0x2a, 0x00, 0x08, 0x00] }, + // NOTE several file types match this, which we handle in TryDisambiguate: DNG, GPR (GoPro), KDC (Kodak), 3FR (Hasselblad) + //{ FileType.Arw, "II"u8.ToArray(), [0x2a, 0x00, 0x08, 0x00] }, { FileType.Crw, "II"u8.ToArray(), [0x1a, 0x00, 0x00, 0x00], "HEAPCCDR"u8.ToArray() }, { FileType.Cr2, "II"u8.ToArray(), [0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52] }, // NOTE this doesn't work for NEF as it incorrectly flags many other TIFF files as being NEF @@ -59,22 +60,77 @@ public static class FileTypeDetector { FileType.Rw2, "II"u8.ToArray(), [0x55, 0x00] }, }; - private static readonly IEnumerable _fixedCheckers = new ITypeChecker[] + private static FileType TryDisambiguate(FileType detectedFileType, string fileName) { + if (detectedFileType == FileType.Tiff) + { + var extension = GetExtension(); + + if (MemoryExtensions.Equals(extension, ".arw".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Arw; + } + else if (MemoryExtensions.Equals(extension, ".dng".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Dng; + } + else if (MemoryExtensions.Equals(extension, ".gpr".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.GoPro; + } + else if (MemoryExtensions.Equals(extension, ".kdc".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Kdc; + } + else if (MemoryExtensions.Equals(extension, ".nef".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Nef; + } + else if (MemoryExtensions.Equals(extension, ".3fr".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.ThreeFR; + } + else if (MemoryExtensions.Equals(extension, ".pef".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Pef; + } + else if (MemoryExtensions.Equals(extension, ".srw".AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return FileType.Srw; + } + } + + return detectedFileType; + + ReadOnlySpan GetExtension() + { + int index = fileName.LastIndexOf('.'); + + if (index == -1) + { + return []; + } + + return fileName.AsSpan(index); + } + } + + private static readonly IReadOnlyList _checkers = + [ new QuickTimeTypeChecker(), new RiffTypeChecker(), new TgaTypeChecker(), new MpegAudioTypeChecker() - }; + ]; private static readonly int _bytesNeeded = Math.Max( - _root.MaxDepth, - _fixedCheckers.Max(checker => checker.ByteCount)); + _root.MaxDepth, + _checkers.Max(checker => checker.ByteCount)); /// Examines the file's first bytes and estimates the file's type. /// Stream does not support seeking. /// An IO error occurred, or the input stream ended unexpectedly. - public static FileType DetectFileType(Stream stream) + public static FileType DetectFileType(Stream stream, string? fileName = null) { if (!stream.CanSeek) throw new ArgumentException("Must support seek", nameof(stream)); @@ -83,30 +139,41 @@ public static FileType DetectFileType(Stream stream) try { - var bytesRead = stream.Read(bytes, 0, bytes.Length); + var bytesRead = stream.Read(bytes, 0, _bytesNeeded); if (bytesRead == 0) + { + // Stream was empty return FileType.Unknown; + } + // Rewind the stream to where we started. We are only peeking at the data. stream.Seek(-bytesRead, SeekOrigin.Current); + // Use the prefix trie to match bytes. var fileType = _root.Find(bytes); - if (fileType == FileType.Unknown) + if (fileType != FileType.Unknown) { - foreach (var fixedChecker in _fixedCheckers) + // Found + return fileName is null ? fileType : TryDisambiguate(fileType, fileName); + } + + foreach (var checker in _checkers) + { + if (bytesRead >= checker.ByteCount) { - if (bytesRead >= fixedChecker.ByteCount) - { - fileType = fixedChecker.CheckType(bytes); + fileType = checker.CheckType(bytes); - if (fileType != FileType.Unknown) - return fileType; + if (fileType != FileType.Unknown) + { + // Found + return fileType; } } } - return fileType; + return FileType.Unknown; } finally {