using System; using System.Collections; using System.IO; using com.drew.lang; using com.drew.metadata; using com.drew.imaging.jpg; using com.utils; /// /// This class was first written by Drew Noakes in Java. /// /// This is public domain software - that is, you can do whatever you want /// with it, and include it software that is licensed under the GNU or the /// BSD license, or whatever other licence you choose, including proprietary /// closed source licenses. I do ask that you leave this header in tact. /// /// If you make modifications to this code that you think would benefit the /// wider community, please send me a copy and I'll post it on my site. /// /// If you make use of this code, Drew Noakes will appreciate hearing /// about it: drew@drewnoakes.com /// /// Latest Java version of this software kept at /// http://drewnoakes.com/ /// /// The C# class was made by Ferret Renaud: /// renaud91@free.fr /// If you find a bug in the C# code, feel free to mail me. /// namespace com.drew.metadata.iptc { /// /// The Iptc reader class /// public class IptcReader : MetadataReader { /* public const int DIRECTORY_IPTC = 2; public const int ENVELOPE_RECORD = 1; public const int APPLICATION_RECORD_2 = 2; public const int APPLICATION_RECORD_3 = 3; public const int APPLICATION_RECORD_4 = 4; public const int APPLICATION_RECORD_5 = 5; public const int APPLICATION_RECORD_6 = 6; public const int PRE_DATA_RECORD = 7; public const int DATA_RECORD = 8; public const int POST_DATA_RECORD = 9; */ /// /// The Iptc data segment /// private readonly byte[] _data; /// /// Creates a new IptcReader for the specified Jpeg jpegFile. /// /// where to read public IptcReader(FileInfo jpegFile) : this( new JpegSegmentReader(jpegFile).ReadSegment( JpegSegmentReader.SEGMENT_APPD)) { } /// /// Constructor of the object /// /// the data to read public IptcReader(byte[] data) { _data = data; } /// /// Performs the Iptc data extraction, returning a new instance of Metadata. /// /// a new instance of Metadata public Metadata Extract() { return Extract(new Metadata()); } /// /// Extracts metadata /// /// where to add metadata /// the metadata found public Metadata Extract(Metadata metadata) { if (_data == null) { return metadata; } Directory directory = metadata.GetDirectory(Type.GetType("com.drew.metadata.iptc.IptcDirectory")); // find start of data int offset = 0; try { while (offset < _data.Length - 1 && Get32Bits(offset) != 0x1c02) { offset++; } } catch (MetadataException ) { directory.AddError( "Couldn't find start of Iptc data (invalid segment)"); return metadata; } // for each tag while (offset < _data.Length) { // identifies start of a tag if (_data[offset] != 0x1c) { break; } // we need at least five bytes left to read a tag if ((offset + 5) >= _data.Length) { break; } offset++; int directoryType; int tagType; int tagByteCount; try { directoryType = _data[offset++]; tagType = _data[offset++]; tagByteCount = Get32Bits(offset); } catch (MetadataException ) { directory.AddError( "Iptc data segment ended mid-way through tag descriptor"); return metadata; } offset += 2; if ((offset + tagByteCount) > _data.Length) { directory.AddError( "data for tag extends beyond end of iptc segment"); break; } ProcessTag(directory, directoryType, tagType, offset, tagByteCount); offset += tagByteCount; } return metadata; } /// /// Returns an int calculated from two bytes of data at the specified offset (MSB, LSB). /// /// position within the data buffer to read first byte /// the 32 bit int value, between 0x0000 and 0xFFFF private int Get32Bits(int offset) { if (offset >= _data.Length) { throw new MetadataException("Attempt to read bytes from outside Iptc data buffer"); } return ((_data[offset] & 255) << 8) | (_data[offset + 1] & 255); } /// /// This method serves as marsheller of objects for dataset. /// It converts from IPTC octets to relevant java object. /// /// the directory /// the directory type /// the tag type /// the offset /// the tag byte count private void ProcessTag( Directory directory, int directoryType, int tagType, int offset, int tagByteCount) { int tagIdentifier = tagType | (directoryType << 8); if (tagIdentifier == IptcDirectory.TAG_RECORD_VERSION) { // short short shortValue = (short) ((_data[offset] << 8) | _data[offset + 1]); directory.SetObject(tagIdentifier, shortValue); return; } else if (tagIdentifier== IptcDirectory.TAG_URGENCY) { // byte directory.SetObject(tagIdentifier, _data[offset]); return; } else if (tagIdentifier== IptcDirectory.TAG_RELEASE_DATE || tagIdentifier== IptcDirectory.TAG_DATE_CREATED) { // Date object if (tagByteCount >= 8) { String dateStr = Utils.Decode(_data, offset, tagByteCount, false); try { int year = Convert.ToInt32(dateStr.Substring(0, 4)); int month = Convert.ToInt32(dateStr.Substring(4, 2)); //No -1 here; int day = Convert.ToInt32(dateStr.Substring(6, 2)); DateTime date = new DateTime(year, month, day); directory.SetObject(tagIdentifier, date); return; } catch (FormatException ) { // fall through and we'll store whatever was there as a String } } } else if (tagIdentifier== IptcDirectory.TAG_RELEASE_TIME || tagIdentifier== IptcDirectory.TAG_TIME_CREATED) { // time... } // If no special handling by now, treat it as a string String str; if (tagByteCount < 1) { str = ""; } else { str = Utils.Decode(_data, offset, tagByteCount, false); } if (directory.ContainsTag(tagIdentifier)) { String[] oldStrings; String[] newStrings; try { oldStrings = directory.GetStringArray(tagIdentifier); } catch (MetadataException ) { oldStrings = null; } if (oldStrings == null) { newStrings = new String[1]; } else { newStrings = new String[oldStrings.Length + 1]; for (int i = 0; i < oldStrings.Length; i++) { newStrings[i] = oldStrings[i]; } } newStrings[newStrings.Length - 1] = str; directory.SetObject(tagIdentifier, newStrings); } else { directory.SetObject(tagIdentifier, str); } } } }