public override bool CustomProcessTag(int tagOffset, ICollection <int> processedIfdOffsets, IndexedReader reader, int tagId, int byteCount)
        {
            // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad.
            if (tagId == 0)
            {
                if (CurrentDirectory.ContainsTag(tagId))
                {
                    // Let it go through for now. Some directories handle it, some don't.
                    return(false);
                }

                // Skip over 0x0000 tags that don't have any associated bytes. No idea what it contains in this case, if anything.
                if (byteCount == 0)
                {
                    return(true);
                }
            }

            // Custom processing for the Makernote tag
            if (tagId == ExifDirectoryBase.TagMakernote && CurrentDirectory is ExifSubIfdDirectory)
            {
                return(ProcessMakernote(tagOffset, processedIfdOffsets, reader));
            }

            // Custom processing for embedded IPTC data
            if (tagId == ExifDirectoryBase.TagIptcNaa && CurrentDirectory is ExifIfd0Directory)
            {
                // NOTE Adobe sets type 4 for IPTC instead of 7
                if (reader.GetSByte(tagOffset) == 0x1c)
                {
                    var iptcBytes     = reader.GetBytes(tagOffset, byteCount);
                    var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(iptcBytes), iptcBytes.Length);
                    iptcDirectory.Parent = CurrentDirectory;
                    Directories.Add(iptcDirectory);
                    return(true);
                }
                return(false);
            }

            // Custom processing for embedded XMP data
            if (tagId == ExifDirectoryBase.TagApplicationNotes && CurrentDirectory is ExifIfd0Directory)
            {
                var xmpDirectory = new XmpReader().Extract(reader.GetNullTerminatedBytes(tagOffset, byteCount));
                xmpDirectory.Parent = CurrentDirectory;
                Directories.Add(xmpDirectory);
                return(true);
            }

            if (HandlePrintIM(CurrentDirectory, tagId))
            {
                var dirPrintIm = new PrintIMDirectory();
                dirPrintIm.Parent = CurrentDirectory;
                Directories.Add(dirPrintIm);
                ProcessPrintIM(dirPrintIm, tagOffset, reader, byteCount);
                return(true);
            }

            // Note: these also appear in TryEnterSubIfd because some are IFD pointers while others begin immediately
            // for the same directories
            if (CurrentDirectory is OlympusMakernoteDirectory)
            {
                switch (tagId)
                {
                case OlympusMakernoteDirectory.TagEquipment:
                    PushDirectory(new OlympusEquipmentMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagCameraSettings:
                    PushDirectory(new OlympusCameraSettingsMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawDevelopment:
                    PushDirectory(new OlympusRawDevelopmentMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawDevelopment2:
                    PushDirectory(new OlympusRawDevelopment2MakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagImageProcessing:
                    PushDirectory(new OlympusImageProcessingMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagFocusInfo:
                    PushDirectory(new OlympusFocusInfoMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawInfo:
                    PushDirectory(new OlympusRawInfoMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagMainInfo:
                    PushDirectory(new OlympusMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);
                }
            }

            if (CurrentDirectory is PanasonicRawIfd0Directory)
            {
                // these contain binary data with specific offsets, and can't be processed as regular ifd's.
                // The binary data is broken into 'fake' tags and there is a pattern.
                switch (tagId)
                {
                case PanasonicRawIfd0Directory.TagWbInfo:
                    var dirWbInfo = new PanasonicRawWbInfoDirectory();
                    dirWbInfo.Parent = CurrentDirectory;
                    Directories.Add(dirWbInfo);
                    ProcessBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2);
                    return(true);

                case PanasonicRawIfd0Directory.TagWbInfo2:
                    var dirWbInfo2 = new PanasonicRawWbInfo2Directory();
                    dirWbInfo2.Parent = CurrentDirectory;
                    Directories.Add(dirWbInfo2);
                    ProcessBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3);
                    return(true);

                case PanasonicRawIfd0Directory.TagDistortionInfo:
                    var dirDistort = new PanasonicRawDistortionDirectory();
                    dirDistort.Parent = CurrentDirectory;
                    Directories.Add(dirDistort);
                    ProcessBinary(dirDistort, tagOffset, reader, byteCount);
                    return(true);
                }
            }

            // Panasonic RAW sometimes contains an embedded version of the data as a JPG file.
            if (tagId == PanasonicRawIfd0Directory.TagJpgFromRaw && CurrentDirectory is PanasonicRawIfd0Directory)
            {
                var jpegrawbytes = reader.GetBytes(tagOffset, byteCount);

                // Extract information from embedded image since it is metadata-rich
                var jpegmem       = new MemoryStream(jpegrawbytes);
                var jpegDirectory = Jpeg.JpegMetadataReader.ReadMetadata(jpegmem);
                foreach (var dir in jpegDirectory)
                {
                    dir.Parent = CurrentDirectory;
                    Directories.Add(dir);
                }
                return(true);
            }

            return(false);
        }