📄 exifmetadataprocessor.cs
字号:
//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
// Copyright (c) Telligent Systems Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections;
namespace CommunityServer.Galleries.Components
{
public class ExifMetadataProcessor : IMetadataProcessor
{
#region Private Members
private bool _isMotorollaByteOrder;
private int TIFF_HEADER_START_OFFSET = 6;
private const int TAG_EXIF_OFFSET = 0x8769;
private const int TAG_INTEROP_OFFSET = 0xA005;
#endregion
#region Constructors
public ExifMetadataProcessor() : base() { }
public ExifMetadataProcessor(byte[] data) : base(data) { }
public ExifMetadataProcessor(byte[] data, Hashtable metadata) : base(data, metadata) { }
#endregion
#region Public Methods
public override void Extract()
{
if(dataSegment == null)
return;
// once we know there's some data, create the directory and start working on it
if(dataSegment.Length <= 14)
return; //throw new Exception("Exif data segment must contain at least 14 bytes");
if(!"Exif\0\0".Equals(Decode(0, 6)))
return; //throw new Exception("Exif data segment doesn't begin with 'Exif'");
// this should be either "MM" or "II"
string byteOrderIdentifier = Decode(6, 2);
if(!SetByteOrder(byteOrderIdentifier))
return; //throw new Exception("Unclear distinction between Motorola/Intel byte ordering");
// Check the next two values for correctness.
if(Get16Bits(8) != 0x2a)
return; //throw new Exception("Invalid Exif start - should have 0x2A at offSet 8 in Exif header");
int firstDirectoryOffSet = Get32Bits(10) + TIFF_HEADER_START_OFFSET;
// David Ekholm sent an digital camera image that has this problem
if(firstDirectoryOffSet >= dataSegment.Length - 1)
{
//throw new Exception("First exif directory offSet is beyond end of Exif data segment");
// First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
firstDirectoryOffSet = 14;
}
// 0th IFD (we merge with Exif IFD)
Process(firstDirectoryOffSet);
}
#endregion
#region Private Methods
private void Process(int segmentOffset)
{
if (segmentOffset >= dataSegment.Length || segmentOffset < 0)
return; //throw new Exception("Ignored directory marked to start outside data segement");
// First two bytes in the IFD are the tag count
int dirTagCount = Get16Bits(segmentOffset);
if (!isDirectoryLengthValid(segmentOffset))
return; //throw new Exception("Illegally sized directory");
// Handle each tag in this directory
for (int dirEntry = 0; dirEntry < dirTagCount; dirEntry++)
{
try
{
int dirEntryOffSet = CalculateDirectoryEntryOffSet(segmentOffset, dirEntry);
ExifProperty tagType = (ExifProperty)Get16Bits(dirEntryOffSet);
MetaFormat format = (MetaFormat)Get16Bits(dirEntryOffSet + 2);
// If it is an unknown format, then force it to be a string
if((int)format > 12)
format = MetaFormat.STRING;
int componentCount = Get32Bits(dirEntryOffSet + 4);
int byteCount = componentCount * BYTES_PER_FORMAT[(int)format];
int tagValueOffset = CalculateTagValueOffSet(byteCount, dirEntryOffSet);
// Calculate the aValue as an offSet for cases where the tag represents directory
int subdirOffset = TIFF_HEADER_START_OFFSET + Get32Bits(tagValueOffset);
switch((int)tagType)
{
case TAG_EXIF_OFFSET:
Process(subdirOffset);
break;
case TAG_INTEROP_OFFSET:
Process(subdirOffset);
break;
case (int)ExifProperty.WindowsAuthor:
case (int)ExifProperty.WindowsComments:
case (int)ExifProperty.WindowsKeywords:
case (int)ExifProperty.WindowsSubject:
case (int)ExifProperty.WindowsTitle:
metadata.Add(tagType, Decode(tagValueOffset, componentCount, true));
break;
default:
switch(format)
{
case MetaFormat.STRING:
case MetaFormat.UNDEFINED:
metadata.Add(tagType, DecodeStringValue(tagValueOffset, componentCount));
break;
case MetaFormat.URATIONAL:
case MetaFormat.SRATIONAL:
metadata.Add(tagType, new Rational( Get32Bits(tagValueOffset), Get32Bits(tagValueOffset + 4) ));
break;
case MetaFormat.BYTE:
case MetaFormat.SBYTE:
if(componentCount == 1)
metadata.Add(tagType, dataSegment[tagValueOffset]);
else
{
int[] bytes = new int[componentCount];
for (int i = 0; i < componentCount; i++)
bytes[i] = dataSegment[tagValueOffset + i];
metadata.Add(tagType, bytes);
}
break;
case MetaFormat.SINGLE:
case MetaFormat.DOUBLE:
metadata.Add(tagType, (int)dataSegment[tagValueOffset]);
break;
case MetaFormat.USHORT:
case MetaFormat.SSHORT:
metadata.Add(tagType, (short)Get16Bits(tagValueOffset));
break;
case MetaFormat.SLONG:
case MetaFormat.ULONG:
metadata.Add(tagType, Get32Bits(tagValueOffset));
break;
}
break;
}
}
catch { }
}
}
#endregion
#region Helper Functions
private int Get16Bits(int offset)
{
if(offset < 0 || offset >= dataSegment.Length)
throw new IndexOutOfRangeException( string.Format( "attempt to read data outside of exif segment (index {0} where max index is {1}", offset, (dataSegment.Length-1) ) );
if(_isMotorollaByteOrder)
// Motorola big first
return (dataSegment[offset] << 8 & 0xFF00) | (dataSegment[offset + 1] & 0xFF);
else
// Intel ordering
return (dataSegment[offset + 1] << 8 & 0xFF00) | (dataSegment[offset] & 0xFF);
}
private int Get32Bits(int offset)
{
if(offset < 0 || offset >= dataSegment.Length)
throw new IndexOutOfRangeException( string.Format( "attempt to read data outside of exif segment (index {0} where max index is {1}", offset, (dataSegment.Length-1) ) );
if(_isMotorollaByteOrder)
// Motorola big first
return (int) ( ((uint)(dataSegment[offset] << 24 & 0xFF000000))
| ((uint)(dataSegment[offset + 1] << 16 & 0xFF0000))
| ((uint)(dataSegment[offset + 2] << 8 & 0xFF00))
| ((uint)(dataSegment[offset + 3] & 0xFF)));
else
// Intel ordering
return (int) ( ((uint)(dataSegment[offset + 3] << 24 & 0xFF000000))
| ((uint)(dataSegment[offset + 2] << 16 & 0xFF0000))
| ((uint)(dataSegment[offset + 1] << 8 & 0xFF00))
| ((uint)(dataSegment[offset] & 0xFF)));
}
private int CalculateTagValueOffSet(int byteCount, int dirEntryOffSet)
{
if(byteCount > 4)
{
// If its bigger than 4 bytes, the dir entry contains an offSet.
// TODO if we're reading FujiFilm makernote tags, the offSet is relative to the start of the makernote itself, not the TIFF segment
int offSetVal = Get32Bits(dirEntryOffSet + 8);
if (offSetVal + byteCount > dataSegment.Length)
{
// Bogus pointer offSet and / or bytecount aValue
return -1; // signal error
}
return TIFF_HEADER_START_OFFSET + offSetVal;
}
else
// 4 bytes or less and aValue is in the dir entry itself
return dirEntryOffSet + 8;
}
private int CalculateDirectoryEntryOffSet(int ifdStartOffSet, int entryNumber)
{
return (ifdStartOffSet + 2 + (12 * entryNumber));
}
private bool isDirectoryLengthValid(int dirStartOffSet)
{
int dirTagCount = Get16Bits(dirStartOffSet);
int dirLength = (2 + (12 * dirTagCount) + 4);
return !(dirLength + dirStartOffSet + TIFF_HEADER_START_OFFSET >= dataSegment.Length);
}
private bool SetByteOrder(string byteOrderIdentifier)
{
if ("MM".Equals(byteOrderIdentifier))
_isMotorollaByteOrder = true;
else if ("II".Equals(byteOrderIdentifier))
_isMotorollaByteOrder = false;
else
return false;
return true;
}
#endregion
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -