Browse Source

Read and validate the TIFF file header.

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
1c9f39918f
  1. 6
      src/ImageSharp.Formats.Tiff/TiffConstants.cs
  2. 71
      src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs
  3. 79
      tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs
  4. 14
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs

6
src/ImageSharp.Formats.Tiff/TiffConstants.cs

@ -10,17 +10,17 @@ namespace ImageSharp.Formats
/// <summary>
/// Defines constants defined in the TIFF specification.
/// </summary>
internal static class GifConstants
internal static class TiffConstants
{
/// <summary>
/// Byte order markers for indicating little endian encoding.
/// </summary>
public const ushort ByteOrderLittleEndian = 0x4949;
public const byte ByteOrderLittleEndian = 0x49;
/// <summary>
/// Byte order markers for indicating big endian encoding.
/// </summary>
public const ushort ByteOrderBigEndian = 0x4D4D;
public const byte ByteOrderBigEndian = 0x4D;
/// <summary>
/// Magic number used within the image file header to identify a TIFF format file.

71
src/ImageSharp.Formats.Tiff/TiffDecoderCore.cs

@ -20,6 +20,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly IDecoderOptions options;
/// <summary>
/// A flag indicating if the file is encoded in little-endian or big-endian format.
/// </summary>
private bool isLittleEndian;
/// <summary>
/// Initializes a new instance of the <see cref="TiffDecoderCore" /> class.
/// </summary>
@ -46,6 +51,8 @@ namespace ImageSharp.Formats
where TColor : struct, IPixel<TColor>
{
this.InputStream = stream;
uint firstIfdOffset = ReadHeader();
}
/// <summary>
@ -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);
}
}
}

79
tests/ImageSharp.Formats.Tiff.Tests/Formats/Tiff/TiffDecoderHeaderTests.cs

@ -0,0 +1,79 @@
// <copyright file="TiffDecoderHeaderTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
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<ImageFormatException>(() => { 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<ImageFormatException>(() => { 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<ImageFormatException>(() => { 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<Color>(image, stream, null);
}
}
}

14
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 };
}
}
}
}
Loading…
Cancel
Save