diff --git a/src/ImageSharp.Formats.Tiff/TiffConstants.cs b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
index e5d2df044..0f9145208 100644
--- a/src/ImageSharp.Formats.Tiff/TiffConstants.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffConstants.cs
@@ -10,17 +10,17 @@ namespace ImageSharp.Formats
///
/// Defines constants defined in the TIFF specification.
///
- internal static class GifConstants
+ internal static class TiffConstants
{
///
/// Byte order markers for indicating little endian encoding.
///
- public const ushort ByteOrderLittleEndian = 0x4949;
+ public const byte ByteOrderLittleEndian = 0x49;
///
/// Byte order markers for indicating big endian encoding.
///
- public const ushort ByteOrderBigEndian = 0x4D4D;
+ public const byte ByteOrderBigEndian = 0x4D;
///
/// Magic number used within the image file header to identify a TIFF format file.
diff --git a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
index e9bbb650b..18a5c3f5e 100644
--- a/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
@@ -20,6 +20,11 @@ namespace ImageSharp.Formats
///
private readonly IDecoderOptions options;
+ ///
+ /// A flag indicating if the file is encoded in little-endian or big-endian format.
+ ///
+ private bool isLittleEndian;
+
///
/// Initializes a new instance of the class.
///
@@ -46,6 +51,8 @@ namespace ImageSharp.Formats
where TColor : struct, IPixel
{
this.InputStream = stream;
+
+ uint firstIfdOffset = ReadHeader();
}
///
@@ -54,5 +61,69 @@ namespace ImageSharp.Formats
public void Dispose()
{
}
+
+ private uint ReadHeader()
+ {
+ byte[] headerBytes = new byte[8];
+ ReadBytes(headerBytes, 8);
+
+ if (headerBytes[0] == TiffConstants.ByteOrderLittleEndian && headerBytes[1] == TiffConstants.ByteOrderLittleEndian)
+ isLittleEndian = true;
+ else if (headerBytes[0] != TiffConstants.ByteOrderBigEndian && headerBytes[1] != TiffConstants.ByteOrderBigEndian)
+ throw new ImageFormatException("Invalid TIFF file header.");
+
+ if (ToUInt16(headerBytes, 2) != TiffConstants.HeaderMagicNumber)
+ throw new ImageFormatException("Invalid TIFF file header.");
+
+ uint firstIfdOffset = ToUInt32(headerBytes, 4);
+ if (firstIfdOffset == 0)
+ throw new ImageFormatException("Invalid TIFF file header.");
+
+ return firstIfdOffset;
+ }
+
+ private byte[] ReadBytes(byte[] buffer, int count)
+ {
+ int offset = 0;
+
+ while (count > 0)
+ {
+ int bytesRead = InputStream.Read(buffer, offset, count);
+
+ if (bytesRead == 0)
+ break;
+
+ offset += bytesRead;
+ count -= bytesRead;
+ }
+
+ return buffer;
+ }
+
+ private Int16 ToInt16(byte[] bytes, int offset)
+ {
+ if (isLittleEndian)
+ return (short)(bytes[offset + 0] | (bytes[offset + 1] << 8));
+ else
+ return (short)((bytes[offset + 0] << 8) | bytes[offset + 1]);
+ }
+
+ private Int32 ToInt32(byte[] bytes, int offset)
+ {
+ if (isLittleEndian)
+ return bytes[offset + 0] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24);
+ else
+ return (bytes[offset + 0] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3];
+ }
+
+ private UInt32 ToUInt32(byte[] bytes, int offset)
+ {
+ return (uint)ToInt32(bytes, offset);
+ }
+
+ private UInt16 ToUInt16(byte[] bytes, int offset)
+ {
+ return (ushort)ToInt16(bytes, offset);
+ }
}
}
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs
new file mode 100644
index 000000000..00b826ef0
--- /dev/null
+++ b/tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs
@@ -0,0 +1,79 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System.IO;
+ using System.Linq;
+ using Xunit;
+
+ using ImageSharp.Formats;
+
+ public class TiffDecoderHeaderTests
+ {
+ public static object[][] IsLittleEndianValues = new[] { new object[] { false },
+ new object[] { true } };
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void Decode_ThrowsException_WithInvalidByteOrderMarkers(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenHeader()
+ {
+ FirstIfd = new TiffGenIfd(),
+ ByteOrderMarker = 0x1234
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoder decoder = new TiffDecoder();
+
+ ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); });
+
+ Assert.Equal("Invalid TIFF file header.", e.Message);
+ }
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void Decode_ThrowsException_WithIncorrectMagicNumber(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenHeader()
+ {
+ FirstIfd = new TiffGenIfd(),
+ MagicNumber = 32
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoder decoder = new TiffDecoder();
+
+ ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); });
+
+ Assert.Equal("Invalid TIFF file header.", e.Message);
+ }
+
+ [Theory]
+ [MemberData(nameof(IsLittleEndianValues))]
+ public void Decode_ThrowsException_WithNoIfdZero(bool isLittleEndian)
+ {
+ Stream stream = new TiffGenHeader()
+ {
+ FirstIfd = null
+ }
+ .ToStream(isLittleEndian);
+
+ TiffDecoder decoder = new TiffDecoder();
+
+ ImageFormatException e = Assert.Throws(() => { TestDecode(decoder, stream); });
+
+ Assert.Equal("Invalid TIFF file header.", e.Message);
+ }
+
+ private void TestDecode(TiffDecoder decoder, Stream stream)
+ {
+ Configuration.Default.AddImageFormat(new TiffFormat());
+ Image image = new Image(1,1);
+ decoder.Decode(image, stream, null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs
index b270ff208..95322dc66 100644
--- a/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs
+++ b/tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs
@@ -32,11 +32,17 @@ namespace ImageSharp.Tests
bytes.AddUInt32(0);
var headerData = new TiffGenDataBlock(bytes.ToArray());
- var firstIfdData = FirstIfd.GetData(isLittleEndian);
- firstIfdData.First().AddReference(headerData.Bytes, 4);
-
- return new [] { headerData }.Concat(firstIfdData);
+ if (FirstIfd != null)
+ {
+ var firstIfdData = FirstIfd.GetData(isLittleEndian);
+ firstIfdData.First().AddReference(headerData.Bytes, 4);
+ return new [] { headerData }.Concat(firstIfdData);
+ }
+ else
+ {
+ return new [] { headerData };
+ }
}
}
}
\ No newline at end of file