private static void ProcessTag(SequentialReader reader, Directory directory, int directoryType, int tagType, int tagByteCount)
        {
            var tagIdentifier = tagType | (directoryType << 8);

            // Some images have been seen that specify a zero byte tag, which cannot be of much use.
            // We elect here to completely ignore the tag. The IPTC specification doesn't mention
            // anything about the interpretation of this situation.
            // https://raw.githubusercontent.com/wiki/drewnoakes/metadata-extractor/docs/IPTC-IIMV4.2.pdf
            if (tagByteCount == 0)
            {
                directory.Set(tagIdentifier, string.Empty);
                return;
            }

            switch (tagIdentifier)
            {
            case IptcDirectory.TagCodedCharacterSet:
            {
                var bytes   = reader.GetBytes(tagByteCount);
                var charset = Iso2022Converter.ConvertEscapeSequenceToEncodingName(bytes);
                if (charset == null)
                {
                    // Unable to determine the charset, so fall through and treat tag as a regular string
                    charset = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                }
                directory.Set(tagIdentifier, charset);
                return;
            }

            case IptcDirectory.TagEnvelopeRecordVersion:
            case IptcDirectory.TagApplicationRecordVersion:
            case IptcDirectory.TagFileVersion:
            case IptcDirectory.TagArmVersion:
            case IptcDirectory.TagProgramVersion:
            {
                // short
                if (tagByteCount == 2)
                {
                    var shortValue = reader.GetUInt16();
                    reader.Skip(tagByteCount - 2);
                    directory.Set(tagIdentifier, shortValue);
                    return;
                }
                break;
            }

            case IptcDirectory.TagUrgency:
            {
                // byte
                directory.Set(tagIdentifier, reader.GetByte());
                reader.Skip(tagByteCount - 1);
                return;
            }
            }

            // If we haven't returned yet, treat it as a string
            // NOTE that there's a chance we've already loaded the value as a string above, but failed to parse the value
            var      encodingName = directory.GetString(IptcDirectory.TagCodedCharacterSet);
            Encoding?encoding     = null;

            if (encodingName != null)
            {
                try
                {
                    encoding = Encoding.GetEncoding(encodingName);
                }
                catch (ArgumentException)
                { }
            }

            StringValue str;

            if (encoding != null)
            {
                str = reader.GetStringValue(tagByteCount, encoding);
            }
            else
            {
                var bytes = reader.GetBytes(tagByteCount);
                encoding = Iso2022Converter.GuessEncoding(bytes);
                str      = new StringValue(bytes, encoding);
            }

            if (directory.ContainsTag(tagIdentifier))
            {
                // this fancy string[] business avoids using an ArrayList for performance reasons
                var oldStrings = directory.GetStringValueArray(tagIdentifier);

                StringValue[] newStrings;
                if (oldStrings == null)
                {
                    // TODO hitting this block means any prior value(s) are discarded
                    newStrings = new StringValue[1];
                }
                else
                {
                    newStrings = new StringValue[oldStrings.Length + 1];
                    Array.Copy(oldStrings, 0, newStrings, 0, oldStrings.Length);
                }
                newStrings[newStrings.Length - 1] = str;
                directory.Set(tagIdentifier, newStrings);
            }
            else
            {
                directory.Set(tagIdentifier, str);
            }
        }
        private static void ProcessTag([NotNull] SequentialReader reader, [NotNull] Directory directory, int directoryType, int tagType, int tagByteCount)
        {
            var tagIdentifier = tagType | (directoryType << 8);

            // Some images have been seen that specify a zero byte tag, which cannot be of much use.
            // We elect here to completely ignore the tag. The IPTC specification doesn't mention
            // anything about the interpretation of this situation.
            // https://raw.githubusercontent.com/wiki/drewnoakes/metadata-extractor/docs/IPTC-IIMV4.2.pdf
            if (tagByteCount == 0)
            {
                directory.Set(tagIdentifier, string.Empty);
                return;
            }

            string str = null;

            switch (tagIdentifier)
            {
            case IptcDirectory.TagCodedCharacterSet:
            {
                var bytes   = reader.GetBytes(tagByteCount);
                var charset = Iso2022Converter.ConvertEscapeSequenceToEncodingName(bytes);
                if (charset == null)
                {
                    // Unable to determine the charset, so fall through and treat tag as a regular string
                    str = Encoding.UTF8.GetString(bytes);
                    break;
                }
                directory.Set(tagIdentifier, charset);
                return;
            }

            case IptcDirectory.TagEnvelopeRecordVersion:
            case IptcDirectory.TagApplicationRecordVersion:
            case IptcDirectory.TagFileVersion:
            case IptcDirectory.TagArmVersion:
            case IptcDirectory.TagProgramVersion:
            {
                // short
                if (tagByteCount >= 2)
                {
                    var shortValue = reader.GetUInt16();
                    reader.Skip(tagByteCount - 2);
                    directory.Set(tagIdentifier, shortValue);
                    return;
                }
                break;
            }

            case IptcDirectory.TagUrgency:
            {
                // byte
                directory.Set(tagIdentifier, reader.GetByte());
                reader.Skip(tagByteCount - 1);
                return;
            }

            case IptcDirectory.TagReleaseDate:
            case IptcDirectory.TagDateCreated:
            case IptcDirectory.TagDigitalDateCreated:
            {
                // Date object
                if (tagByteCount >= 8)
                {
                    str = reader.GetString(tagByteCount);
                    Debug.Assert(str.Length >= 8);

                    int year, month, day;
                    if (int.TryParse(str.Substring(0, 4), out year) &&
                        int.TryParse(str.Substring(4, 2), out month) &&
                        int.TryParse(str.Substring(6, 2), out day) &&
                        DateUtil.IsValidDate(year, month, day))
                    {
                        directory.Set(tagIdentifier, new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Unspecified));
                        return;
                    }
                }
                else
                {
                    // fall through and we'll process the 'string' value below
                    reader.Skip(tagByteCount);
                }
                break;
            }
            }

            // If we haven't returned yet, treat it as a string
            // NOTE that there's a chance we've already loaded the value as a string above, but failed to parse the value
            if (str == null)
            {
                var      encodingName = directory.GetString(IptcDirectory.TagCodedCharacterSet);
                Encoding encoding     = null;
                if (encodingName != null)
                {
                    try
                    {
                        encoding = Encoding.GetEncoding(encodingName);
                    }
                    catch { }
                }

                var bytes = reader.GetBytes(tagByteCount);

                if (encoding == null)
                {
                    encoding = Iso2022Converter.GuessEncoding(bytes);
                }

                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }

                str = encoding.GetString(bytes);
            }

            if (directory.ContainsTag(tagIdentifier))
            {
                // this fancy string[] business avoids using an ArrayList for performance reasons
                var oldStrings = directory.GetStringArray(tagIdentifier);

                string[] newStrings;
                if (oldStrings == null)
                {
                    // TODO hitting this block means any prior value(s) are discarded
                    newStrings = new string[1];
                }
                else
                {
                    newStrings = new string[oldStrings.Length + 1];
                    Array.Copy(oldStrings, 0, newStrings, 0, oldStrings.Length);
                }
                newStrings[newStrings.Length - 1] = str;
                directory.Set(tagIdentifier, newStrings);
            }
            else
            {
                directory.Set(tagIdentifier, str);
            }
        }