/// <exception cref="PngProcessingException"/>
        public PngHeader([NotNull] byte[] bytes)
        {
            if (bytes.Length != 13)
            {
                throw new PngProcessingException("PNG header chunk must have exactly 13 data bytes");
            }

            var reader = new SequentialByteArrayReader(bytes);

            ImageWidth    = reader.GetInt32();
            ImageHeight   = reader.GetInt32();
            BitsPerSample = reader.GetByte();

            var colorTypeNumber = reader.GetByte();
            var colorType       = PngColorType.FromNumericValue(colorTypeNumber);

            if (colorType == null)
            {
                throw new PngProcessingException("Unexpected PNG color type: " + colorTypeNumber);
            }
            ColorType = colorType;

            CompressionType = reader.GetByte();
            FilterMethod    = reader.GetByte();
            InterlaceMethod = reader.GetByte();
        }
        /// <summary>
        /// Inserts a new Xmp App1 segment to the output memory stream.
        /// The segment will be inserted -2 bytes from the current position.
        /// </summary>
        /// <param name="xmp">Xmp document to be inserted</param>
        /// <param name="input">Stream of bytes in the original file</param>
        /// <param name="output">Stream of bytes in the newly composed file</param>
        /// <param name="nextSegmentIdentifier">The identifier of the segment that began at position -2</param>
        /// <param name="nextSegmentTypeByte">The identifier of the segment that began at position -2</param>
        /// <returns>True, indicating that the Xmp content was inserted</returns>
        private static bool InsertApp1XmpSegment(XDocument xmp, SequentialByteArrayReader input, MemoryStream output, byte nextSegmentIdentifier, byte nextSegmentTypeByte)
        {
            // 1. remember the segment that comes after
            byte[] nextSegmentMarker = new byte[] { nextSegmentIdentifier, nextSegmentTypeByte };

            // 2. open a new App1 segment in place of the recently encountered segment
            output.Seek(-2, SeekOrigin.Current);
            // reference: http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_JPEG_files
            byte[] app1marker = new byte[] { 0xFF, 0xE1 };
            output.Write(app1marker, 0, app1marker.Length);

            // 3. prepare the new Xmp segment
            byte[] segmentBytes = ByteArrayFromXmpXDocument(xmp);
            byte[] lengthMark   = BytesFromSegmentLength(segmentBytes.Length, input.IsMotorolaByteOrder);
            byte   highByte     = lengthMark[0];
            byte   lowByte      = lengthMark[1];

            // 4. write the new segment to the output
            output.WriteByte(highByte);
            output.WriteByte(lowByte);
            output.Write(segmentBytes, 0, segmentBytes.Length);

            // 5. now write the segment marker that was replaced
            // (in the next iteration it will just proceed with that segment)
            output.Write(nextSegmentMarker, 0, nextSegmentMarker.Length);
            return(true);
        }
Exemple #3
0
        public string GetBackgroundColorDescription()
        {
            var bytes = Directory.GetByteArray(PngDirectory.TagBackgroundColor);

            if (bytes == null || !Directory.TryGetInt32(PngDirectory.TagColorType, out int colorType))
            {
                return(null);
            }

            var reader = new SequentialByteArrayReader(bytes);

            try
            {
                switch (colorType)
                {
                case 0:
                case 4:
                    // TODO do we need to normalise these based upon the bit depth?
                    return($"Greyscale Level {reader.GetUInt16()}");

                case 2:
                case 6:
                    return($"R {reader.GetUInt16()}, G {reader.GetUInt16()}, B {reader.GetUInt16()}");

                case 3:
                    return($"Palette Index {reader.GetByte()}");
                }
            }
            catch (IOException)
            {
                return(null);
            }

            return(null);
        }
Exemple #4
0
        public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
        {
            JpegDirectory directory = new JpegDirectory();

            metadata.AddDirectory(directory);
            // The value of TAG_COMPRESSION_TYPE is determined by the segment type found
            directory.SetInt(JpegDirectory.TagCompressionType, segmentType.byteValue - JpegSegmentType.Sof0.byteValue);
            SequentialReader reader = new SequentialByteArrayReader(segmentBytes);

            try
            {
                directory.SetInt(JpegDirectory.TagDataPrecision, reader.GetUInt8());
                directory.SetInt(JpegDirectory.TagImageHeight, reader.GetUInt16());
                directory.SetInt(JpegDirectory.TagImageWidth, reader.GetUInt16());
                short componentCount = reader.GetUInt8();
                directory.SetInt(JpegDirectory.TagNumberOfComponents, componentCount);
                // for each component, there are three bytes of data:
                // 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
                // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
                // 3 - Quantization table number
                for (int i = 0; i < (int)componentCount; i++)
                {
                    int           componentId             = reader.GetUInt8();
                    int           samplingFactorByte      = reader.GetUInt8();
                    int           quantizationTableNumber = reader.GetUInt8();
                    JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
                    directory.SetObject(JpegDirectory.TagComponentData1 + i, component);
                }
            }
            catch (IOException ex)
            {
                directory.AddError(ex.Message);
            }
        }
Exemple #5
0
        /// <exception cref="Com.Drew.Imaging.Png.PngProcessingException"/>
        public PngHeader(sbyte[] bytes)
        {
            if (bytes.Length != 13)
            {
                throw new PngProcessingException("PNG header chunk must have 13 data bytes");
            }
            SequentialReader reader = new SequentialByteArrayReader(bytes);

            try
            {
                _imageWidth    = reader.GetInt32();
                _imageHeight   = reader.GetInt32();
                _bitsPerSample = reader.GetInt8();
                sbyte        colorTypeNumber = reader.GetInt8();
                PngColorType colorType       = PngColorType.FromNumericValue(colorTypeNumber);
                if (colorType == null)
                {
                    throw new PngProcessingException("Unexpected PNG color type: " + colorTypeNumber);
                }
                _colorType       = colorType;
                _compressionType = reader.GetInt8();
                _filterMethod    = reader.GetInt8();
                _interlaceMethod = reader.GetInt8();
            }
            catch (IOException e)
            {
                // Should never happen
                throw new PngProcessingException(e);
            }
        }
Exemple #6
0
        public string GetBackgroundColorDescription()
        {
            var bytes = Directory.GetByteArray(PngDirectory.TagBackgroundColor);

            if (bytes == null)
            {
                return(null);
            }

            var reader = new SequentialByteArrayReader(bytes);

            try
            {
                // TODO do we need to normalise these based upon the bit depth?
                switch (bytes.Length)
                {
                case 1:
                    return($"Palette Index {reader.GetByte()}");

                case 2:
                    return($"Greyscale Level {reader.GetUInt16()}");

                case 6:
                    return($"R {reader.GetUInt16()}, G {reader.GetUInt16()}, B {reader.GetUInt16()}");
                }
            }
            catch (IOException)
            {
                return(null);
            }

            return(null);
        }
        /// <summary>
        /// Copies an App1 segment and may replace it with Xmp.
        /// </summary>
        /// <param name="xmp">Xmp content that shall be used</param>
        /// <param name="input">Stream of bytes in the original file</param>
        /// <param name="output">Stream of bytes in the newly composed file</param>
        /// <param name="wroteXmp"></param>
        /// <returns>indicates if the Xmp content was written to this segment</returns>
        private static bool CopyOrUpdateApp1Segment(XDocument xmp, SequentialByteArrayReader input, MemoryStream output, bool wroteXmp)
        {
            // 1. read the segment index that encodes the segment length
            byte highByte      = input.GetByte();
            byte lowByte       = input.GetByte();
            int  segmentLength = SegmentLengthFromBytes(highByte, lowByte, input.IsMotorolaByteOrder);

            // 2. read the rest of the segment
            byte[] segmentBytes = input.GetBytes(segmentLength);

            // 3. if this is the XMP segment, overwrite it with the new segment
            int preambleLength = XmpReader.JpegSegmentPreamble.Length;

            if (segmentBytes.Length >= preambleLength && XmpReader.JpegSegmentPreamble.Equals(Encoding.UTF8.GetString(segmentBytes, 0, preambleLength), StringComparison.OrdinalIgnoreCase))
            {
                // overwrite the payload
                segmentBytes = ByteArrayFromXmpXDocument(xmp);
                // and update the segment length!!
                byte[] lengthMark = BytesFromSegmentLength(segmentBytes.Length, input.IsMotorolaByteOrder);
                highByte = lengthMark[0];
                lowByte  = lengthMark[1];
                // indicate that the Xmp was written to this segment
                wroteXmp = true;
            }

            // 4. only now write the entire segment (index + payload)
            output.WriteByte(highByte);
            output.WriteByte(lowByte);
            output.Write(segmentBytes, 0, segmentBytes.Length);

            return(wroteXmp);
        }
Exemple #8
0
        /// <exception cref="PngProcessingException"/>
        public PngChromaticities(byte[] bytes)
        {
            if (bytes.Length != 8 * 4)
            {
                throw new PngProcessingException("Invalid number of bytes");
            }

            var reader = new SequentialByteArrayReader(bytes);

            try
            {
                WhitePointX = reader.GetInt32();
                WhitePointY = reader.GetInt32();
                RedX        = reader.GetInt32();
                RedY        = reader.GetInt32();
                GreenX      = reader.GetInt32();
                GreenY      = reader.GetInt32();
                BlueX       = reader.GetInt32();
                BlueY       = reader.GetInt32();
            }
            catch (IOException ex)
            {
                throw new PngProcessingException(ex);
            }
        }
        private void ProcessCameraSettings([NotNull] byte[] bytes)
        {
            var reader = new SequentialByteArrayReader(bytes);
            var count  = bytes.Length / 4;

            for (var i = 0; i < count; i++)
            {
                Set(CameraSettings.Offset + i, reader.GetInt32());
            }
        }
Exemple #10
0
        /// <exception cref="PngProcessingException"/>
        public PngHeader(byte[] bytes)
        {
            if (bytes.Length != 13)
            {
                throw new PngProcessingException("PNG header chunk must have exactly 13 data bytes");
            }

            var reader = new SequentialByteArrayReader(bytes);

            ImageWidth      = reader.GetInt32();
            ImageHeight     = reader.GetInt32();
            BitsPerSample   = reader.GetByte();
            ColorType       = PngColorType.FromNumericValue(reader.GetByte());
            CompressionType = reader.GetByte();
            FilterMethod    = reader.GetByte();
            InterlaceMethod = reader.GetByte();
        }
        private void ProcessCameraSettings(sbyte[] bytes)
        {
            SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);

            reader.SetMotorolaByteOrder(true);
            int count = bytes.Length / 4;

            try
            {
                for (int i = 0; i < count; i++)
                {
                    int value = reader.GetInt32();
                    SetInt(OlympusMakernoteDirectory.CameraSettings.Offset + i, value);
                }
            }
            catch (IOException e)
            {
                // Should never happen, given that we check the length of the bytes beforehand.
                Sharpen.Runtime.PrintStackTrace(e);
            }
        }
        private void ProcessCameraSettings([NotNull] byte[] bytes)
        {
            var reader = new SequentialByteArrayReader(bytes)
            {
                IsMotorolaByteOrder = true
            };
            var count = bytes.Length / 4;

            try
            {
                for (var i = 0; i < count; i++)
                {
                    Set(CameraSettings.Offset + i, reader.GetInt32());
                }
            }
            catch (IOException e)
            {
                // Should never happen, given that we check the length of the bytes beforehand.
                Console.WriteLine(e);
            }
        }
Exemple #13
0
        public virtual string GetBackgroundColorDescription()
        {
            sbyte[] bytes     = _directory.GetByteArray(PngDirectory.TagBackgroundColor);
            int?    colorType = _directory.GetInteger(PngDirectory.TagColorType);

            if (bytes == null || colorType == null)
            {
                return(null);
            }
            SequentialReader reader = new SequentialByteArrayReader(bytes);

            try
            {
                switch (colorType)
                {
                case 0:
                case 4:
                {
                    // TODO do we need to normalise these based upon the bit depth?
                    return(Sharpen.Extensions.StringFormat("Greyscale Level %d", reader.GetUInt16()));
                }

                case 2:
                case 6:
                {
                    return(Sharpen.Extensions.StringFormat("R %d, G %d, B %d", reader.GetUInt16(), reader.GetUInt16(), reader.GetUInt16()));
                }

                case 3:
                {
                    return(Sharpen.Extensions.StringFormat("Palette Index %d", reader.GetUInt8()));
                }
                }
            }
            catch (IOException)
            {
                return(null);
            }
            return(null);
        }
Exemple #14
0
        /// <summary>Reads JPEG SOF values and returns them in a <see cref="JpegDirectory"/>.</summary>
        public JpegDirectory Extract(JpegSegment segment)
        {
            var directory = new JpegDirectory();

            // The value of TagCompressionType is determined by the segment type found
            directory.Set(JpegDirectory.TagCompressionType, (int)segment.Type - (int)JpegSegmentType.Sof0);

            SequentialReader reader = new SequentialByteArrayReader(segment.Bytes);

            try
            {
                directory.Set(JpegDirectory.TagDataPrecision, reader.GetByte());
                directory.Set(JpegDirectory.TagImageHeight, reader.GetUInt16());
                directory.Set(JpegDirectory.TagImageWidth, reader.GetUInt16());

                var componentCount = reader.GetByte();

                directory.Set(JpegDirectory.TagNumberOfComponents, componentCount);

                // For each component, there are three bytes of data:
                // 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
                // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
                // 3 - Quantization table number

                for (var i = 0; i < componentCount; i++)
                {
                    var componentId             = reader.GetByte();
                    var samplingFactorByte      = reader.GetByte();
                    var quantizationTableNumber = reader.GetByte();
                    var component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
                    directory.Set(JpegDirectory.TagComponentData1 + i, component);
                }
            }
            catch (IOException ex)
            {
                directory.AddError(ex.Message);
            }

            return(directory);
        }
Exemple #15
0
        /// <exception cref="PngProcessingException"/>
        /// <exception cref="System.IO.IOException"/>
        private static IEnumerable <Directory> ProcessChunk([NotNull] PngChunk chunk)
        {
            var chunkType = chunk.ChunkType;
            var bytes     = chunk.Bytes;

            if (chunkType == PngChunkType.IHDR)
            {
                var header    = new PngHeader(bytes);
                var directory = new PngDirectory(PngChunkType.IHDR);
                directory.Set(PngDirectory.TagImageWidth, header.ImageWidth);
                directory.Set(PngDirectory.TagImageHeight, header.ImageHeight);
                directory.Set(PngDirectory.TagBitsPerSample, header.BitsPerSample);
                directory.Set(PngDirectory.TagColorType, header.ColorType.NumericValue);
                directory.Set(PngDirectory.TagCompressionType, header.CompressionType);
                directory.Set(PngDirectory.TagFilterMethod, header.FilterMethod);
                directory.Set(PngDirectory.TagInterlaceMethod, header.InterlaceMethod);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.PLTE)
            {
                var directory = new PngDirectory(PngChunkType.PLTE);
                directory.Set(PngDirectory.TagPaletteSize, bytes.Length / 3);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tRNS)
            {
                var directory = new PngDirectory(PngChunkType.tRNS);
                directory.Set(PngDirectory.TagPaletteHasTransparency, 1);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.sRGB)
            {
                int srgbRenderingIntent = unchecked ((sbyte)bytes[0]);
                var directory           = new PngDirectory(PngChunkType.sRGB);
                directory.Set(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.cHRM)
            {
                var chromaticities = new PngChromaticities(bytes);
                var directory      = new PngChromaticitiesDirectory();
                directory.Set(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.WhitePointX);
                directory.Set(PngChromaticitiesDirectory.TagWhitePointY, chromaticities.WhitePointY);
                directory.Set(PngChromaticitiesDirectory.TagRedX, chromaticities.RedX);
                directory.Set(PngChromaticitiesDirectory.TagRedY, chromaticities.RedY);
                directory.Set(PngChromaticitiesDirectory.TagGreenX, chromaticities.GreenX);
                directory.Set(PngChromaticitiesDirectory.TagGreenY, chromaticities.GreenY);
                directory.Set(PngChromaticitiesDirectory.TagBlueX, chromaticities.BlueX);
                directory.Set(PngChromaticitiesDirectory.TagBlueY, chromaticities.BlueY);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.gAMA)
            {
                var gammaInt  = ByteConvert.ToInt32BigEndian(bytes);
                var directory = new PngDirectory(PngChunkType.gAMA);
                directory.Set(PngDirectory.TagGamma, gammaInt / 100000.0);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.iCCP)
            {
                var reader      = new SequentialByteArrayReader(bytes);
                var profileName = reader.GetNullTerminatedStringValue(maxLengthBytes: 79);
                var directory   = new PngDirectory(PngChunkType.iCCP);
                directory.Set(PngDirectory.TagIccProfileName, profileName);
                var compressionMethod = reader.GetSByte();
                if (compressionMethod == 0)
                {
                    // Only compression method allowed by the spec is zero: deflate
                    // This assumes 1-byte-per-char, which it is by spec.
                    var bytesLeft         = bytes.Length - profileName.Bytes.Length - 2;
                    var compressedProfile = reader.GetBytes(bytesLeft);
                    using (var inflaterStream = new DeflateStream(new MemoryStream(compressedProfile), CompressionMode.Decompress))
//                    using (var inflaterStream = new InflaterInputStream(new MemoryStream(compressedProfile)))
                    {
                        var iccDirectory = new IccReader().Extract(new IndexedCapturingReader(inflaterStream));
                        iccDirectory.Parent = directory;
                        yield return(iccDirectory);
                    }
                }
                else
                {
                    directory.AddError("Invalid compression method value");
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.bKGD)
            {
                var directory = new PngDirectory(PngChunkType.bKGD);
                directory.Set(PngDirectory.TagBackgroundColor, bytes);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tEXt)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var keyword   = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var bytesLeft = bytes.Length - keyword.Length - 1;
                var value     = reader.GetNullTerminatedStringValue(bytesLeft, _latin1Encoding);

                var textPairs = new List <KeyValuePair> {
                    new KeyValuePair(keyword, value)
                };
                var directory = new PngDirectory(PngChunkType.iTXt);
                directory.Set(PngDirectory.TagTextualData, textPairs);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.zTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionMethod = reader.GetSByte();

                var    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - 1;
                byte[] textBytes = null;
                if (compressionMethod == 0)
                {
                    using (var inflaterStream = new DeflateStream(new MemoryStream(bytes, bytes.Length - bytesLeft, bytesLeft), CompressionMode.Decompress))
                        using (var decompStream = new MemoryStream())
                        {
#if !NET35
                            inflaterStream.CopyTo(decompStream);
#else
                            byte[] buffer = new byte[256];
                            int    count;
                            int    totalBytes = 0;
                            while ((count = inflaterStream.Read(buffer, 0, 256)) > 0)
                            {
                                decompStream.Write(buffer, 0, count);
                                totalBytes += count;
                            }
#endif
                            textBytes = decompStream.ToArray();
                        }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.zTXt);
                    directory.AddError("Invalid compression method value");
                    yield return(directory);
                }
                if (textBytes != null)
                {
                    if (keyword == "XML:com.adobe.xmp")
                    {
                        // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                        yield return(new XmpReader().Extract(textBytes));
                    }
                    else
                    {
                        var textPairs = new List <KeyValuePair> {
                            new KeyValuePair(keyword, new StringValue(textBytes, _latin1Encoding))
                        };
                        var directory = new PngDirectory(PngChunkType.zTXt);
                        directory.Set(PngDirectory.TagTextualData, textPairs);
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.iTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionFlag   = reader.GetSByte();
                var compressionMethod = reader.GetSByte();

                // TODO we currently ignore languageTagBytes and translatedKeywordBytes
                var languageTagBytes       = reader.GetNullTerminatedBytes(bytes.Length);
                var translatedKeywordBytes = reader.GetNullTerminatedBytes(bytes.Length);

                var    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - languageTagBytes.Length - 1 - translatedKeywordBytes.Length - 1;
                byte[] textBytes = null;
                if (compressionFlag == 0)
                {
                    textBytes = reader.GetNullTerminatedBytes(bytesLeft);
                }
                else if (compressionFlag == 1)
                {
                    if (compressionMethod == 0)
                    {
                        using (var inflaterStream = new DeflateStream(new MemoryStream(bytes, bytes.Length - bytesLeft, bytesLeft), CompressionMode.Decompress))
                            using (var decompStream = new MemoryStream())
                            {
#if !NET35
                                inflaterStream.CopyTo(decompStream);
#else
                                byte[] buffer = new byte[256];
                                int    count;
                                int    totalBytes = 0;
                                while ((count = inflaterStream.Read(buffer, 0, 256)) > 0)
                                {
                                    decompStream.Write(buffer, 0, count);
                                    totalBytes += count;
                                }
#endif
                                textBytes = decompStream.ToArray();
                            }
                    }
                    else
                    {
                        var directory = new PngDirectory(PngChunkType.iTXt);
                        directory.AddError("Invalid compression method value");
                        yield return(directory);
                    }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.iTXt);
                    directory.AddError("Invalid compression flag value");
                    yield return(directory);
                }

                if (textBytes != null)
                {
                    if (keyword == "XML:com.adobe.xmp")
                    {
                        // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                        yield return(new XmpReader().Extract(textBytes));
                    }
                    else
                    {
                        var textPairs = new List <KeyValuePair> {
                            new KeyValuePair(keyword, new StringValue(textBytes, _latin1Encoding))
                        };
                        var directory = new PngDirectory(PngChunkType.iTXt);
                        directory.Set(PngDirectory.TagTextualData, textPairs);
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.tIME)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var year      = reader.GetUInt16();
                var month     = reader.GetByte();
                int day       = reader.GetByte();
                int hour      = reader.GetByte();
                int minute    = reader.GetByte();
                int second    = reader.GetByte();
                var directory = new PngDirectory(PngChunkType.tIME);
                try
                {
                    var time = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified);
                    directory.Set(PngDirectory.TagLastModificationTime, time);
                }
                catch (ArgumentOutOfRangeException e)
                {
                    directory.AddError("Error constructing DateTime: " + e.Message);
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.pHYs)
            {
                var reader         = new SequentialByteArrayReader(bytes);
                var pixelsPerUnitX = reader.GetInt32();
                var pixelsPerUnitY = reader.GetInt32();
                var unitSpecifier  = reader.GetSByte();
                var directory      = new PngDirectory(PngChunkType.pHYs);
                directory.Set(PngDirectory.TagPixelsPerUnitX, pixelsPerUnitX);
                directory.Set(PngDirectory.TagPixelsPerUnitY, pixelsPerUnitY);
                directory.Set(PngDirectory.TagUnitSpecifier, unitSpecifier);
                yield return(directory);
            }
            else if (chunkType.Equals(PngChunkType.sBIT))
            {
                var directory = new PngDirectory(PngChunkType.sBIT);
                directory.Set(PngDirectory.TagSignificantBits, bytes);
                yield return(directory);
            }
        }
Exemple #16
0
        private static BitReader CreateReader(params byte[] sourceData)
        {
            var sr = new SequentialByteArrayReader(sourceData);

            return(new BitReader(sr));
        }
        /// <exception cref="Com.Drew.Imaging.Png.PngProcessingException"/>
        /// <exception cref="System.IO.IOException"/>
        private static void ProcessChunk([NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] PngChunk chunk)
        {
            PngChunkType chunkType = chunk.GetChunkType();

            sbyte[] bytes = chunk.GetBytes();
            if (chunkType.Equals(PngChunkType.Ihdr))
            {
                PngHeader    header    = new PngHeader(bytes);
                PngDirectory directory = new PngDirectory(PngChunkType.Ihdr);
                directory.SetInt(PngDirectory.TagImageWidth, header.GetImageWidth());
                directory.SetInt(PngDirectory.TagImageHeight, header.GetImageHeight());
                directory.SetInt(PngDirectory.TagBitsPerSample, header.GetBitsPerSample());
                directory.SetInt(PngDirectory.TagColorType, header.GetColorType().GetNumericValue());
                directory.SetInt(PngDirectory.TagCompressionType, header.GetCompressionType());
                directory.SetInt(PngDirectory.TagFilterMethod, header.GetFilterMethod());
                directory.SetInt(PngDirectory.TagInterlaceMethod, header.GetInterlaceMethod());
                metadata.AddDirectory(directory);
            }
            else
            {
                if (chunkType.Equals(PngChunkType.Plte))
                {
                    PngDirectory directory = new PngDirectory(PngChunkType.Plte);
                    directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3);
                    metadata.AddDirectory(directory);
                }
                else
                {
                    if (chunkType.Equals(PngChunkType.tRNS))
                    {
                        PngDirectory directory = new PngDirectory(PngChunkType.tRNS);
                        directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1);
                        metadata.AddDirectory(directory);
                    }
                    else
                    {
                        if (chunkType.Equals(PngChunkType.sRGB))
                        {
                            int          srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8();
                            PngDirectory directory           = new PngDirectory(PngChunkType.sRGB);
                            directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                            metadata.AddDirectory(directory);
                        }
                        else
                        {
                            if (chunkType.Equals(PngChunkType.cHRM))
                            {
                                PngChromaticities          chromaticities = new PngChromaticities(bytes);
                                PngChromaticitiesDirectory directory      = new PngChromaticitiesDirectory();
                                directory.SetInt(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.GetWhitePointX());
                                directory.SetInt(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.GetWhitePointX());
                                directory.SetInt(PngChromaticitiesDirectory.TagRedX, chromaticities.GetRedX());
                                directory.SetInt(PngChromaticitiesDirectory.TagRedY, chromaticities.GetRedY());
                                directory.SetInt(PngChromaticitiesDirectory.TagGreenX, chromaticities.GetGreenX());
                                directory.SetInt(PngChromaticitiesDirectory.TagGreenY, chromaticities.GetGreenY());
                                directory.SetInt(PngChromaticitiesDirectory.TagBlueX, chromaticities.GetBlueX());
                                directory.SetInt(PngChromaticitiesDirectory.TagBlueY, chromaticities.GetBlueY());
                                metadata.AddDirectory(directory);
                            }
                            else
                            {
                                if (chunkType.Equals(PngChunkType.gAMA))
                                {
                                    int          gammaInt  = new SequentialByteArrayReader(bytes).GetInt32();
                                    PngDirectory directory = new PngDirectory(PngChunkType.gAMA);
                                    directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0);
                                    metadata.AddDirectory(directory);
                                }
                                else
                                {
                                    if (chunkType.Equals(PngChunkType.iCCP))
                                    {
                                        SequentialReader reader      = new SequentialByteArrayReader(bytes);
                                        string           profileName = reader.GetNullTerminatedString(79);
                                        PngDirectory     directory   = new PngDirectory(PngChunkType.iCCP);
                                        directory.SetString(PngDirectory.TagIccProfileName, profileName);
                                        sbyte compressionMethod = reader.GetInt8();
                                        if (compressionMethod == 0)
                                        {
                                            // Only compression method allowed by the spec is zero: deflate
                                            // This assumes 1-byte-per-char, which it is by spec.
                                            int                 bytesLeft         = bytes.Length - profileName.Length - 2;
                                            sbyte[]             compressedProfile = reader.GetBytes(bytesLeft);
                                            InflaterInputStream inflateStream     = new InflaterInputStream(new ByteArrayInputStream(compressedProfile));
                                            new IccReader().Extract(new RandomAccessStreamReader(inflateStream), metadata);
                                            inflateStream.Close();
                                        }
                                        metadata.AddDirectory(directory);
                                    }
                                    else
                                    {
                                        if (chunkType.Equals(PngChunkType.bKGD))
                                        {
                                            PngDirectory directory = new PngDirectory(PngChunkType.bKGD);
                                            directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes);
                                            metadata.AddDirectory(directory);
                                        }
                                        else
                                        {
                                            if (chunkType.Equals(PngChunkType.tEXt))
                                            {
                                                SequentialReader reader        = new SequentialByteArrayReader(bytes);
                                                string           keyword       = reader.GetNullTerminatedString(79);
                                                int    bytesLeft               = bytes.Length - keyword.Length - 1;
                                                string value                   = reader.GetNullTerminatedString(bytesLeft);
                                                IList <KeyValuePair> textPairs = new AList <KeyValuePair>();
                                                textPairs.Add(new KeyValuePair(keyword, value));
                                                PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                directory.SetObject(PngDirectory.TagTextualData, textPairs);
                                                metadata.AddDirectory(directory);
                                            }
                                            else
                                            {
                                                if (chunkType.Equals(PngChunkType.iTXt))
                                                {
                                                    SequentialReader reader            = new SequentialByteArrayReader(bytes);
                                                    string           keyword           = reader.GetNullTerminatedString(79);
                                                    sbyte            compressionFlag   = reader.GetInt8();
                                                    sbyte            compressionMethod = reader.GetInt8();
                                                    string           languageTag       = reader.GetNullTerminatedString(bytes.Length);
                                                    string           translatedKeyword = reader.GetNullTerminatedString(bytes.Length);
                                                    int    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - languageTag.Length - 1 - translatedKeyword.Length - 1;
                                                    string text      = null;
                                                    if (compressionFlag == 0)
                                                    {
                                                        text = reader.GetNullTerminatedString(bytesLeft);
                                                    }
                                                    else
                                                    {
                                                        if (compressionFlag == 1)
                                                        {
                                                            if (compressionMethod == 0)
                                                            {
                                                                text = StringUtil.FromStream(new InflaterInputStream(new ByteArrayInputStream(bytes, bytes.Length - bytesLeft, bytesLeft)));
                                                            }
                                                            else
                                                            {
                                                                PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                                directory.AddError("Invalid compression method value");
                                                                metadata.AddDirectory(directory);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                            directory.AddError("Invalid compression flag value");
                                                            metadata.AddDirectory(directory);
                                                        }
                                                    }
                                                    if (text != null)
                                                    {
                                                        if (keyword.Equals("XML:com.adobe.xmp"))
                                                        {
                                                            // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                                                            new XmpReader().Extract(text, metadata);
                                                        }
                                                        else
                                                        {
                                                            IList <KeyValuePair> textPairs = new AList <KeyValuePair>();
                                                            textPairs.Add(new KeyValuePair(keyword, text));
                                                            PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                            directory.SetObject(PngDirectory.TagTextualData, textPairs);
                                                            metadata.AddDirectory(directory);
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    if (chunkType.Equals(PngChunkType.tIME))
                                                    {
                                                        SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);
                                                        int year   = reader.GetUInt16();
                                                        int month  = reader.GetUInt8() - 1;
                                                        int day    = reader.GetUInt8();
                                                        int hour   = reader.GetUInt8();
                                                        int minute = reader.GetUInt8();
                                                        int second = reader.GetUInt8();
                                                        Sharpen.Calendar calendar = Sharpen.Calendar.GetInstance(Sharpen.Extensions.GetTimeZone("UTC"));
                                                        //noinspection MagicConstant
                                                        calendar.Set(year, month, day, hour, minute, second);
                                                        PngDirectory directory = new PngDirectory(PngChunkType.tIME);
                                                        directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime());
                                                        metadata.AddDirectory(directory);
                                                    }
                                                    else
                                                    {
                                                        if (chunkType.Equals(PngChunkType.pHYs))
                                                        {
                                                            SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);
                                                            int          pixelsPerUnitX      = reader.GetInt32();
                                                            int          pixelsPerUnitY      = reader.GetInt32();
                                                            sbyte        unitSpecifier       = reader.GetInt8();
                                                            PngDirectory directory           = new PngDirectory(PngChunkType.pHYs);
                                                            directory.SetInt(PngDirectory.TagPixelsPerUnitX, pixelsPerUnitX);
                                                            directory.SetInt(PngDirectory.TagPixelsPerUnitY, pixelsPerUnitY);
                                                            directory.SetInt(PngDirectory.TagUnitSpecifier, unitSpecifier);
                                                            metadata.AddDirectory(directory);
                                                        }
                                                        else
                                                        {
                                                            if (chunkType.Equals(PngChunkType.sBIT))
                                                            {
                                                                PngDirectory directory = new PngDirectory(PngChunkType.sBIT);
                                                                directory.SetByteArray(PngDirectory.TagSignificantBits, bytes);
                                                                metadata.AddDirectory(directory);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Exemple #18
0
        protected override void Populate(WavFormatDirectory directory, byte[] payload)
        {
            var reader = new SequentialByteArrayReader(payload, isMotorolaByteOrder: false);

            Populate(directory, reader, payload.Length);
        }
        public static MemoryStream SubstituteXmp([NotNull] byte[] original, [NotNull] XDocument xmp)
        {
            SequentialByteArrayReader input = new SequentialByteArrayReader(original);
            MemoryStream output             = new MemoryStream();

            try
            {
                // The Xmp-substitution loops over the original bytes and copies them into a
                // MemoryStream (ms).  If an App1-segment containing Xmp data is encountered,
                // the new Xmp bytes are written to the MemoryStream instead of the old bytes.
                // If no Xmp-containing App1 is encountered before other segments, a new App1-
                // segment with the Xmp-data is inserted.
                // All non-App1-fragments are just copied as they are.

                bool wroteXmp = false;
                // The following loop scans over the input, copies it to the output and locks in
                // when it encounters a segment marker.
                // Reference: http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_JPEG_files
                while (true)
                {
                    // Find the segment marker. Markers are zero or more 0xFF bytes, followed
                    // by a 0xFF and then a byte not equal to 0x00 or 0xFF.
                    var segmentIdentifier = input.GetByte();
                    output.WriteByte(segmentIdentifier);
                    var segmentTypeByte = input.GetByte();
                    output.WriteByte(segmentTypeByte);

                    // Read until we have a 0xFF byte followed by a byte that is not 0xFF or 0x00
                    while (segmentIdentifier != 0xFF || segmentTypeByte == 0xFF || segmentTypeByte == 0)
                    {
                        segmentIdentifier = segmentTypeByte;
                        segmentTypeByte   = input.GetByte();
                        output.WriteByte(segmentTypeByte);
                    }
                    var segmentType = (JpegSegmentType)segmentTypeByte;


                    // the algorithm above stopped at a segment marker. This may be an opportunity
                    // to update or insert an App1-Xmp segment
                    if (!wroteXmp && segmentType == JpegSegmentType.App1)
                    {
                        // An App1 segment was encountered and might be a candidate for updating the Xmp
                        // Copy the segment (if it already contains Xmp, it will be updated)
                        wroteXmp = CopyOrUpdateApp1Segment(xmp, input, output, wroteXmp);
                    }
                    else if (!wroteXmp && segmentType != JpegSegmentType.Soi && segmentType != JpegSegmentType.App0)
                    {
                        // file begins with Soi (App0) (App1) ...
                        // At this point we have encountered a segment that should not be earlier than an App1.
                        // Also, the files does not contain an App1-Xmp segment yet.
                        // Therefore we must insert a new App1-Xmp segment now.
                        wroteXmp = InsertApp1XmpSegment(xmp, input, output, segmentIdentifier, segmentTypeByte);
                    }

                    // then continues with copying until the next segment or the end of the input
                }
            }
            catch (IOException ex)
            {
                // we expect a IOException when the sequential reader reaches the end of the original data
                if (ex.Message != "End of data reached.")
                {
                    throw new JpegProcessingException("An error occured while trying to write Xml to the buffer.", ex);
                }
            }
            catch (Exception ex)
            {
                throw new JpegProcessingException("An error occured while trying to write Xml to the buffer.", ex);
            }

            return(output);
        }
        public IEnumerable <Directory> Extract(IndexedReader reader)
        {
            var header = reader.GetUInt32(0);

            if (header != 0x46464600)
            {
                var flirHeaderDirectory = new FlirHeaderDirectory();
                flirHeaderDirectory.AddError("Unexpected FFF header bytes.");
                yield return(flirHeaderDirectory);

                yield break;
            }

            var headerDirectory = new FlirHeaderDirectory();

            headerDirectory.Set(FlirHeaderDirectory.TagCreatorSoftware, reader.GetNullTerminatedStringValue(4, 16));
            yield return(headerDirectory);

            var baseIndexOffset = reader.GetUInt32(24);
            var indexCount      = reader.GetUInt32(28);

            var indexOffset = checked ((int)baseIndexOffset);

            for (int i = 0; i < indexCount; i++)
            {
                var mainType   = (FlirMainTagType)reader.GetUInt16(indexOffset);
                var subType    = reader.GetUInt16(indexOffset + 2); // 1=BE, 2=LE, 3=PNG; 1 for other record types
                var version    = reader.GetUInt32(indexOffset + 4);
                var id         = reader.GetUInt32(indexOffset + 8);
                var dataOffset = reader.GetInt32(indexOffset + 12);
                var dataLength = reader.GetInt32(indexOffset + 16);

                if (mainType == FlirMainTagType.Pixels)
                {
                    var reader2 = reader.WithShiftedBaseOffset(dataOffset);

                    // should be 0x0002 if byte order is correct
                    var marker = reader2.GetUInt16(0);
                    if (marker > 0x0100)
                    {
                        reader2 = reader2.WithByteOrder(!reader2.IsMotorolaByteOrder);
                    }

                    var directory = new FlirRawDataDirectory();

                    directory.Set(FlirRawDataDirectory.TagRawThermalImageWidth, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageWidth));
                    directory.Set(FlirRawDataDirectory.TagRawThermalImageHeight, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageHeight));
                    directory.Set(FlirRawDataDirectory.TagRawThermalImageType, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageType));

                    if (ExtractRawThermalImage)
                    {
                        directory.Set(FlirRawDataDirectory.TagRawThermalImage, reader2.GetBytes(32, dataLength - 32));
                    }

                    yield return(directory);
                }
                else if (mainType == FlirMainTagType.BasicData)
                {
                    var reader2 = reader.WithShiftedBaseOffset(dataOffset);

                    // should be 0x0002 if byte order is correct
                    var marker = reader2.GetUInt16(0);
                    if (marker > 0x0100)
                    {
                        reader2 = reader2.WithByteOrder(!reader2.IsMotorolaByteOrder);
                    }

                    var directory = new FlirCameraInfoDirectory();

                    directory.Set(TagEmissivity, reader2.GetFloat32(TagEmissivity));
                    directory.Set(TagObjectDistance, reader2.GetFloat32(TagObjectDistance));
                    directory.Set(TagReflectedApparentTemperature, reader2.GetFloat32(TagReflectedApparentTemperature));
                    directory.Set(TagAtmosphericTemperature, reader2.GetFloat32(TagAtmosphericTemperature));
                    directory.Set(TagIRWindowTemperature, reader2.GetFloat32(TagIRWindowTemperature));
                    directory.Set(TagIRWindowTransmission, reader2.GetFloat32(TagIRWindowTransmission));
                    directory.Set(TagRelativeHumidity, reader2.GetFloat32(TagRelativeHumidity));
                    directory.Set(TagPlanckR1, reader2.GetFloat32(TagPlanckR1));
                    directory.Set(TagPlanckB, reader2.GetFloat32(TagPlanckB));
                    directory.Set(TagPlanckF, reader2.GetFloat32(TagPlanckF));
                    directory.Set(TagAtmosphericTransAlpha1, reader2.GetFloat32(TagAtmosphericTransAlpha1));
                    directory.Set(TagAtmosphericTransAlpha2, reader2.GetFloat32(TagAtmosphericTransAlpha2));
                    directory.Set(TagAtmosphericTransBeta1, reader2.GetFloat32(TagAtmosphericTransBeta1));
                    directory.Set(TagAtmosphericTransBeta2, reader2.GetFloat32(TagAtmosphericTransBeta2));
                    directory.Set(TagAtmosphericTransX, reader2.GetFloat32(TagAtmosphericTransX));
                    directory.Set(TagCameraTemperatureRangeMax, reader2.GetFloat32(TagCameraTemperatureRangeMax));
                    directory.Set(TagCameraTemperatureRangeMin, reader2.GetFloat32(TagCameraTemperatureRangeMin));
                    directory.Set(TagCameraTemperatureMaxClip, reader2.GetFloat32(TagCameraTemperatureMaxClip));
                    directory.Set(TagCameraTemperatureMinClip, reader2.GetFloat32(TagCameraTemperatureMinClip));
                    directory.Set(TagCameraTemperatureMaxWarn, reader2.GetFloat32(TagCameraTemperatureMaxWarn));
                    directory.Set(TagCameraTemperatureMinWarn, reader2.GetFloat32(TagCameraTemperatureMinWarn));
                    directory.Set(TagCameraTemperatureMaxSaturated, reader2.GetFloat32(TagCameraTemperatureMaxSaturated));
                    directory.Set(TagCameraTemperatureMinSaturated, reader2.GetFloat32(TagCameraTemperatureMinSaturated));
                    directory.Set(TagCameraModel, reader2.GetNullTerminatedStringValue(TagCameraModel, 32));
                    directory.Set(TagCameraPartNumber, reader2.GetNullTerminatedStringValue(TagCameraPartNumber, 16));
                    directory.Set(TagCameraSerialNumber, reader2.GetNullTerminatedStringValue(TagCameraSerialNumber, 16));
                    directory.Set(TagCameraSoftware, reader2.GetNullTerminatedStringValue(TagCameraSoftware, 16));
                    directory.Set(TagLensModel, reader2.GetNullTerminatedStringValue(TagLensModel, 32));
                    directory.Set(TagLensPartNumber, reader2.GetNullTerminatedStringValue(TagLensPartNumber, 16));
                    directory.Set(TagLensSerialNumber, reader2.GetNullTerminatedStringValue(TagLensSerialNumber, 16));
                    directory.Set(TagFieldOfView, reader2.GetFloat32(TagFieldOfView));
                    directory.Set(TagFilterModel, reader2.GetNullTerminatedStringValue(TagFilterModel, 16));
                    directory.Set(TagFilterPartNumber, reader2.GetNullTerminatedStringValue(TagFilterPartNumber, 32));
                    directory.Set(TagFilterSerialNumber, reader2.GetNullTerminatedStringValue(TagFilterSerialNumber, 32));
                    directory.Set(TagPlanckO, reader2.GetInt32(TagPlanckO));
                    directory.Set(TagPlanckR2, reader2.GetFloat32(TagPlanckR2));
                    directory.Set(TagRawValueRangeMin, reader2.GetUInt16(TagRawValueRangeMin));
                    directory.Set(TagRawValueRangeMax, reader2.GetUInt16(TagRawValueRangeMax));
                    directory.Set(TagRawValueMedian, reader2.GetUInt16(TagRawValueMedian));
                    directory.Set(TagRawValueRange, reader2.GetUInt16(TagRawValueRange));

                    var dateTimeBytes  = reader2.GetBytes(TagDateTimeOriginal, 10);
                    var dateTimeReader = new SequentialByteArrayReader(dateTimeBytes, isMotorolaByteOrder: false);
                    var tm             = dateTimeReader.GetUInt32();
                    var ss             = dateTimeReader.GetUInt32() & 0xffff;
                    var tz             = dateTimeReader.GetInt16();
                    directory.Set(TagDateTimeOriginal, new DateTimeOffset(DateUtil.FromUnixTime(tm - tz * 60).AddSeconds(ss / 1000d), TimeSpan.FromMinutes(tz)));

                    directory.Set(TagFocusStepCount, reader2.GetUInt16(TagFocusStepCount));
                    directory.Set(TagFocusDistance, reader2.GetFloat32(TagFocusDistance));
                    directory.Set(TagFrameRate, reader2.GetUInt16(TagFrameRate));

                    yield return(directory);
                }

                indexOffset += 32;
            }
        }
Exemple #21
0
        /// <exception cref="PngProcessingException"/>
        /// <exception cref="System.IO.IOException"/>
        private static IEnumerable <Directory> ProcessChunk(PngChunk chunk)
        {
            var chunkType = chunk.ChunkType;
            var bytes     = chunk.Bytes;

            if (chunkType == PngChunkType.IHDR)
            {
                var header    = new PngHeader(bytes);
                var directory = new PngDirectory(PngChunkType.IHDR);
                directory.Set(PngDirectory.TagImageWidth, header.ImageWidth);
                directory.Set(PngDirectory.TagImageHeight, header.ImageHeight);
                directory.Set(PngDirectory.TagBitsPerSample, header.BitsPerSample);
                directory.Set(PngDirectory.TagColorType, header.ColorType.NumericValue);
                directory.Set(PngDirectory.TagCompressionType, header.CompressionType);
                directory.Set(PngDirectory.TagFilterMethod, header.FilterMethod);
                directory.Set(PngDirectory.TagInterlaceMethod, header.InterlaceMethod);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.PLTE)
            {
                var directory = new PngDirectory(PngChunkType.PLTE);
                directory.Set(PngDirectory.TagPaletteSize, bytes.Length / 3);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tRNS)
            {
                var directory = new PngDirectory(PngChunkType.tRNS);
                directory.Set(PngDirectory.TagPaletteHasTransparency, 1);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.sRGB)
            {
                int srgbRenderingIntent = unchecked ((sbyte)bytes[0]);
                var directory           = new PngDirectory(PngChunkType.sRGB);
                directory.Set(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.cHRM)
            {
                var chromaticities = new PngChromaticities(bytes);
                var directory      = new PngChromaticitiesDirectory();
                directory.Set(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.WhitePointX);
                directory.Set(PngChromaticitiesDirectory.TagWhitePointY, chromaticities.WhitePointY);
                directory.Set(PngChromaticitiesDirectory.TagRedX, chromaticities.RedX);
                directory.Set(PngChromaticitiesDirectory.TagRedY, chromaticities.RedY);
                directory.Set(PngChromaticitiesDirectory.TagGreenX, chromaticities.GreenX);
                directory.Set(PngChromaticitiesDirectory.TagGreenY, chromaticities.GreenY);
                directory.Set(PngChromaticitiesDirectory.TagBlueX, chromaticities.BlueX);
                directory.Set(PngChromaticitiesDirectory.TagBlueY, chromaticities.BlueY);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.gAMA)
            {
                var gammaInt  = ByteConvert.ToInt32BigEndian(bytes);
                var directory = new PngDirectory(PngChunkType.gAMA);
                directory.Set(PngDirectory.TagGamma, gammaInt / 100000.0);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.iCCP)
            {
                var reader      = new SequentialByteArrayReader(bytes);
                var profileName = reader.GetNullTerminatedStringValue(maxLengthBytes: 79);
                var directory   = new PngDirectory(PngChunkType.iCCP);
                directory.Set(PngDirectory.TagIccProfileName, profileName);
                var compressionMethod = reader.GetSByte();
                if (compressionMethod == 0)
                {
                    // Only compression method allowed by the spec is zero: deflate
                    // This assumes 1-byte-per-char, which it is by spec.
                    var bytesLeft = bytes.Length - profileName.Bytes.Length - 2;

                    // http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
                    // First two bytes are part of the zlib specification (RFC 1950), not the deflate specification (RFC 1951).
                    reader.Skip(2);
                    bytesLeft -= 2;

                    var compressedProfile = reader.GetBytes(bytesLeft);

                    IccDirectory?iccDirectory = null;
                    Exception?   ex           = null;
                    try
                    {
                        using var inflaterStream = new DeflateStream(new MemoryStream(compressedProfile), CompressionMode.Decompress);
                        iccDirectory             = new IccReader().Extract(new IndexedCapturingReader(inflaterStream));
                        iccDirectory.Parent      = directory;
                    }
                    catch (Exception e)
                    {
                        ex = e;
                    }

                    if (iccDirectory != null)
                    {
                        yield return(iccDirectory);
                    }
                    else if (ex != null)
                    {
                        directory.AddError($"Exception decompressing {nameof(PngChunkType.iCCP)} chunk: {ex.Message}");
                    }
                }
                else
                {
                    directory.AddError("Invalid compression method value");
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.bKGD)
            {
                var directory = new PngDirectory(PngChunkType.bKGD);
                directory.Set(PngDirectory.TagBackgroundColor, bytes);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tEXt)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var keyword   = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var bytesLeft = bytes.Length - keyword.Length - 1;
                var value     = reader.GetNullTerminatedStringValue(bytesLeft, _latin1Encoding);

                var textPairs = new List <KeyValuePair> {
                    new KeyValuePair(keyword, value)
                };
                var directory = new PngDirectory(PngChunkType.tEXt);
                directory.Set(PngDirectory.TagTextualData, textPairs);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.zTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionMethod = reader.GetSByte();

                var bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - 1;
                byte[]? textBytes = null;
                if (compressionMethod == 0)
                {
                    if (!TryDeflate(bytes, bytesLeft, out textBytes, out string?errorMessage))
                    {
                        var directory = new PngDirectory(PngChunkType.zTXt);
                        directory.AddError($"Exception decompressing {nameof(PngChunkType.zTXt)} chunk with keyword \"{keyword}\": {errorMessage}");
                        yield return(directory);
                    }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.zTXt);
                    directory.AddError("Invalid compression method value");
                    yield return(directory);
                }

                if (textBytes != null)
                {
                    foreach (var directory in ProcessTextChunk(keyword, textBytes))
                    {
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.iTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionFlag   = reader.GetSByte();
                var compressionMethod = reader.GetSByte();

                // TODO we currently ignore languageTagBytes and translatedKeywordBytes
                var languageTagBytes       = reader.GetNullTerminatedBytes(bytes.Length);
                var translatedKeywordBytes = reader.GetNullTerminatedBytes(bytes.Length);

                var bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - languageTagBytes.Length - 1 - translatedKeywordBytes.Length - 1;
                byte[]? textBytes = null;
                if (compressionFlag == 0)
                {
                    textBytes = reader.GetNullTerminatedBytes(bytesLeft);
                }
                else if (compressionFlag == 1)
                {
                    if (compressionMethod == 0)
                    {
                        if (!TryDeflate(bytes, bytesLeft, out textBytes, out string?errorMessage))
                        {
                            var directory = new PngDirectory(PngChunkType.iTXt);
                            directory.AddError($"Exception decompressing {nameof(PngChunkType.iTXt)} chunk with keyword \"{keyword}\": {errorMessage}");
                            yield return(directory);
                        }
                    }
                    else
                    {
                        var directory = new PngDirectory(PngChunkType.iTXt);
                        directory.AddError("Invalid compression method value");
                        yield return(directory);
                    }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.iTXt);
                    directory.AddError("Invalid compression flag value");
                    yield return(directory);
                }

                if (textBytes != null)
                {
                    foreach (var directory in ProcessTextChunk(keyword, textBytes))
                    {
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.tIME)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var year      = reader.GetUInt16();
                var month     = reader.GetByte();
                int day       = reader.GetByte();
                int hour      = reader.GetByte();
                int minute    = reader.GetByte();
                int second    = reader.GetByte();
                var directory = new PngDirectory(PngChunkType.tIME);
                if (DateUtil.IsValidDate(year, month, day) && DateUtil.IsValidTime(hour, minute, second))
                {
                    var time = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified);
                    directory.Set(PngDirectory.TagLastModificationTime, time);
                }
                else
                {
                    directory.AddError($"PNG tIME data describes an invalid date/time: year={year} month={month} day={day} hour={hour} minute={minute} second={second}");
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.pHYs)
            {
                var reader         = new SequentialByteArrayReader(bytes);
                var pixelsPerUnitX = reader.GetInt32();
                var pixelsPerUnitY = reader.GetInt32();
                var unitSpecifier  = reader.GetSByte();
                var directory      = new PngDirectory(PngChunkType.pHYs);
                directory.Set(PngDirectory.TagPixelsPerUnitX, pixelsPerUnitX);
                directory.Set(PngDirectory.TagPixelsPerUnitY, pixelsPerUnitY);
                directory.Set(PngDirectory.TagUnitSpecifier, unitSpecifier);
                yield return(directory);
            }
            else if (chunkType.Equals(PngChunkType.sBIT))
            {
                var directory = new PngDirectory(PngChunkType.sBIT);
                directory.Set(PngDirectory.TagSignificantBits, bytes);
                yield return(directory);
            }

            yield break;

            IEnumerable <Directory> ProcessTextChunk(string keyword, byte[] textBytes)
            {
                if (keyword == "XML:com.adobe.xmp")
                {
                    yield return(new XmpReader().Extract(textBytes));
                }
                else if (keyword == "Raw profile type xmp")
                {
                    if (TryProcessRawProfile(out int byteCount))
                    {
                        yield return(new XmpReader().Extract(textBytes, 0, byteCount));
                    }
                    else
                    {
                        yield return(ReadTextDirectory(keyword, textBytes, chunkType));
                    }
                }
                else if (keyword == "Raw profile type exif" || keyword == "Raw profile type APP1")
                {
                    if (TryProcessRawProfile(out _))
                    {
                        foreach (var exifDirectory in new ExifReader().Extract(new ByteArrayReader(textBytes)))
                        {
                            yield return(exifDirectory);
                        }
                    }
                    else
                    {
                        yield return(ReadTextDirectory(keyword, textBytes, chunkType));
                    }
                }
                else if (keyword == "Raw profile type icc" || keyword == "Raw profile type icm")
                {
                    if (TryProcessRawProfile(out _))
                    {
                        yield return(new IccReader().Extract(new ByteArrayReader(textBytes)));
                    }
                    else
                    {
                        yield return(ReadTextDirectory(keyword, textBytes, chunkType));
                    }
                }
                else if (keyword == "Raw profile type iptc")
                {
                    if (TryProcessRawProfile(out int byteCount))
                    {
                        yield return(new IptcReader().Extract(new SequentialByteArrayReader(textBytes), byteCount));
                    }
                    else
                    {
                        yield return(ReadTextDirectory(keyword, textBytes, chunkType));
                    }
                }
                else
                {
                    yield return(ReadTextDirectory(keyword, textBytes, chunkType));
                }

                PngDirectory ReadTextDirectory(string keyword, byte[] textBytes, PngChunkType pngChunkType)
                {
                    var textPairs = new[] { new KeyValuePair(keyword, new StringValue(textBytes, _latin1Encoding)) };
                    var directory = new PngDirectory(pngChunkType);

                    directory.Set(PngDirectory.TagTextualData, textPairs);
                    return(directory);
                }

                bool TryProcessRawProfile(out int byteCount)
                {
                    // Raw profiles have form "\n<name>\n<length>\n<hex>\n"

                    if (textBytes.Length == 0 || textBytes[0] != '\n')
                    {
                        byteCount = default;
                        return(false);
                    }

                    var i = 1;

                    // Skip name
                    while (i < textBytes.Length && textBytes[i] != '\n')
                    {
                        i++;
                    }

                    if (i == textBytes.Length)
                    {
                        byteCount = default;
                        return(false);
                    }

                    // Read length
                    int length = 0;

                    while (true)
                    {
                        i++;
                        var c = (char)textBytes[i];

                        if (c == ' ')
                        {
                            continue;
                        }
                        if (c == '\n')
                        {
                            break;
                        }

                        if (c >= '0' && c <= '9')
                        {
                            length *= 10;
                            length += c - '0';
                        }
                        else
                        {
                            byteCount = default;
                            return(false);
                        }
                    }

                    i++;

                    // We should be at the ASCII-encoded hex data. Walk through the remaining bytes, re-writing as raw bytes
                    // starting at offset zero in the array. We have to skip \n characters.

                    // Validate the data can be correctly parsed before modifying it in-place, because if parsing fails later
                    // consumers may want the unmodified data.

                    // Each row must have 72 characters (36 bytes once decoded) separated by \n
                    const int rowCharCount = 72;
                    int       charsInRow   = rowCharCount;

                    for (int j = i; j < length + i; j++)
                    {
                        byte c = textBytes[j];

                        if (charsInRow-- == 0)
                        {
                            if (c != '\n')
                            {
                                byteCount = default;
                                return(false);
                            }

                            charsInRow = rowCharCount;
                            continue;
                        }

                        if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
                        {
                            byteCount = default;
                            return(false);
                        }
                    }

                    byteCount = length;
                    var writeIndex = 0;

                    charsInRow = rowCharCount;
                    while (length > 0)
                    {
                        var c1 = textBytes[i++];

                        if (charsInRow-- == 0)
                        {
                            Debug.Assert(c1 == '\n');
                            charsInRow = rowCharCount;
                            continue;
                        }

                        var c2 = textBytes[i++];

                        charsInRow--;

                        var n1 = ParseHexNibble(c1);
                        var n2 = ParseHexNibble(c2);

                        length--;
                        textBytes[writeIndex++] = (byte)((n1 << 4) | n2);
                    }

                    return(writeIndex == byteCount);

                    static int ParseHexNibble(int h)
                    {
                        if (h >= '0' && h <= '9')
                        {
                            return(h - '0');
                        }

                        if (h >= 'a' && h <= 'f')
                        {
                            return(10 + (h - 'a'));
                        }

                        if (h >= 'A' && h <= 'F')
                        {
                            return(10 + (h - 'A'));
                        }

                        Debug.Fail("Should not reach here");
                        throw new InvalidOperationException();
                    }
                }
        /// <exception cref="PngProcessingException"/>
        /// <exception cref="System.IO.IOException"/>
        private static IEnumerable <Directory> ProcessChunk([NotNull] PngChunk chunk)
        {
            var chunkType = chunk.ChunkType;
            var bytes     = chunk.Bytes;

            if (chunkType == PngChunkType.IHDR)
            {
                var header    = new PngHeader(bytes);
                var directory = new PngDirectory(PngChunkType.IHDR);
                directory.Set(PngDirectory.TagImageWidth, header.ImageWidth);
                directory.Set(PngDirectory.TagImageHeight, header.ImageHeight);
                directory.Set(PngDirectory.TagBitsPerSample, header.BitsPerSample);
                directory.Set(PngDirectory.TagColorType, header.ColorType.NumericValue);
                directory.Set(PngDirectory.TagCompressionType, header.CompressionType);
                directory.Set(PngDirectory.TagFilterMethod, header.FilterMethod);
                directory.Set(PngDirectory.TagInterlaceMethod, header.InterlaceMethod);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.PLTE)
            {
                var directory = new PngDirectory(PngChunkType.PLTE);
                directory.Set(PngDirectory.TagPaletteSize, bytes.Length / 3);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tRNS)
            {
                var directory = new PngDirectory(PngChunkType.tRNS);
                directory.Set(PngDirectory.TagPaletteHasTransparency, 1);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.sRGB)
            {
                int srgbRenderingIntent = unchecked ((sbyte)bytes[0]);
                var directory           = new PngDirectory(PngChunkType.sRGB);
                directory.Set(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.cHRM)
            {
                var chromaticities = new PngChromaticities(bytes);
                var directory      = new PngChromaticitiesDirectory();
                directory.Set(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.WhitePointX);
                directory.Set(PngChromaticitiesDirectory.TagWhitePointY, chromaticities.WhitePointY);
                directory.Set(PngChromaticitiesDirectory.TagRedX, chromaticities.RedX);
                directory.Set(PngChromaticitiesDirectory.TagRedY, chromaticities.RedY);
                directory.Set(PngChromaticitiesDirectory.TagGreenX, chromaticities.GreenX);
                directory.Set(PngChromaticitiesDirectory.TagGreenY, chromaticities.GreenY);
                directory.Set(PngChromaticitiesDirectory.TagBlueX, chromaticities.BlueX);
                directory.Set(PngChromaticitiesDirectory.TagBlueY, chromaticities.BlueY);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.gAMA)
            {
                var gammaInt  = ByteConvert.ToInt32BigEndian(bytes);
                var directory = new PngDirectory(PngChunkType.gAMA);
                directory.Set(PngDirectory.TagGamma, gammaInt / 100000.0);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.iCCP)
            {
                var reader      = new SequentialByteArrayReader(bytes);
                var profileName = reader.GetNullTerminatedStringValue(maxLengthBytes: 79);
                var directory   = new PngDirectory(PngChunkType.iCCP);
                directory.Set(PngDirectory.TagIccProfileName, profileName);
                var compressionMethod = reader.GetSByte();
                if (compressionMethod == 0)
                {
                    // Only compression method allowed by the spec is zero: deflate
                    // This assumes 1-byte-per-char, which it is by spec.
                    var bytesLeft = bytes.Length - profileName.Bytes.Length - 2;

                    // http://george.chiramattel.com/blog/2007/09/deflatestream-block-length-does-not-match.html
                    // First two bytes are part of the zlib specification (RFC 1950), not the deflate specification (RFC 1951).
                    reader.GetByte(); reader.GetByte();
                    bytesLeft -= 2;

                    var compressedProfile = reader.GetBytes(bytesLeft);

                    IccDirectory iccDirectory = null;
                    Exception    ex           = null;
                    try
                    {
                        using (var inflaterStream = new DeflateStream(new MemoryStream(compressedProfile), CompressionMode.Decompress))
                        {
                            iccDirectory        = new IccReader().Extract(new IndexedCapturingReader(inflaterStream));
                            iccDirectory.Parent = directory;
                        }
                    }
                    catch (Exception e)
                    {
                        ex = e;
                    }

                    if (ex == null)
                    {
                        yield return(iccDirectory);
                    }
                    else
                    {
                        directory.AddError($"Exception decompressing {nameof(PngChunkType.iCCP)} chunk: {ex.Message}");
                    }
                }
                else
                {
                    directory.AddError("Invalid compression method value");
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.bKGD)
            {
                var directory = new PngDirectory(PngChunkType.bKGD);
                directory.Set(PngDirectory.TagBackgroundColor, bytes);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.tEXt)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var keyword   = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var bytesLeft = bytes.Length - keyword.Length - 1;
                var value     = reader.GetNullTerminatedStringValue(bytesLeft, _latin1Encoding);

                var textPairs = new List <KeyValuePair> {
                    new KeyValuePair(keyword, value)
                };
                var directory = new PngDirectory(PngChunkType.tEXt);
                directory.Set(PngDirectory.TagTextualData, textPairs);
                yield return(directory);
            }
            else if (chunkType == PngChunkType.zTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionMethod = reader.GetSByte();

                var    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - 1;
                byte[] textBytes = null;
                if (compressionMethod == 0)
                {
                    using (var inflaterStream = new DeflateStream(new MemoryStream(bytes, bytes.Length - bytesLeft, bytesLeft), CompressionMode.Decompress))
                    {
                        Exception ex = null;
                        try
                        {
                            textBytes = ReadStreamToBytes(inflaterStream);
                        }
                        catch (Exception e)
                        {
                            ex = e;
                        }

                        // Work-around no yield-return from catch blocks
                        if (ex != null)
                        {
                            var directory = new PngDirectory(PngChunkType.zTXt);
                            directory.AddError($"Exception decompressing {nameof(PngChunkType.zTXt)} chunk with keyword \"{keyword}\": {ex.Message}");
                            yield return(directory);
                        }
                    }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.zTXt);
                    directory.AddError("Invalid compression method value");
                    yield return(directory);
                }
                if (textBytes != null)
                {
                    if (keyword == "XML:com.adobe.xmp")
                    {
                        // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                        yield return(new XmpReader().Extract(textBytes));
                    }
                    else
                    {
                        var textPairs = new List <KeyValuePair> {
                            new KeyValuePair(keyword, new StringValue(textBytes, _latin1Encoding))
                        };
                        var directory = new PngDirectory(PngChunkType.zTXt);
                        directory.Set(PngDirectory.TagTextualData, textPairs);
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.iTXt)
            {
                var reader            = new SequentialByteArrayReader(bytes);
                var keyword           = reader.GetNullTerminatedStringValue(maxLengthBytes: 79).ToString(_latin1Encoding);
                var compressionFlag   = reader.GetSByte();
                var compressionMethod = reader.GetSByte();

                // TODO we currently ignore languageTagBytes and translatedKeywordBytes
                var languageTagBytes       = reader.GetNullTerminatedBytes(bytes.Length);
                var translatedKeywordBytes = reader.GetNullTerminatedBytes(bytes.Length);

                var    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - languageTagBytes.Length - 1 - translatedKeywordBytes.Length - 1;
                byte[] textBytes = null;
                if (compressionFlag == 0)
                {
                    textBytes = reader.GetNullTerminatedBytes(bytesLeft);
                }
                else if (compressionFlag == 1)
                {
                    if (compressionMethod == 0)
                    {
                        using (var inflaterStream = new DeflateStream(new MemoryStream(bytes, bytes.Length - bytesLeft, bytesLeft), CompressionMode.Decompress))
                        {
                            Exception ex = null;
                            try
                            {
                                textBytes = ReadStreamToBytes(inflaterStream);
                            }
                            catch (Exception e)
                            {
                                ex = e;
                            }

                            // Work-around no yield-return from catch blocks
                            if (ex != null)
                            {
                                var directory = new PngDirectory(PngChunkType.iTXt);
                                directory.AddError($"Exception decompressing {nameof(PngChunkType.iTXt)} chunk with keyword \"{keyword}\": {ex.Message}");
                                yield return(directory);
                            }
                        }
                    }
                    else
                    {
                        var directory = new PngDirectory(PngChunkType.iTXt);
                        directory.AddError("Invalid compression method value");
                        yield return(directory);
                    }
                }
                else
                {
                    var directory = new PngDirectory(PngChunkType.iTXt);
                    directory.AddError("Invalid compression flag value");
                    yield return(directory);
                }

                if (textBytes != null)
                {
                    if (keyword == "XML:com.adobe.xmp")
                    {
                        // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                        yield return(new XmpReader().Extract(textBytes));
                    }
                    else
                    {
                        var textPairs = new List <KeyValuePair> {
                            new KeyValuePair(keyword, new StringValue(textBytes, _latin1Encoding))
                        };
                        var directory = new PngDirectory(PngChunkType.iTXt);
                        directory.Set(PngDirectory.TagTextualData, textPairs);
                        yield return(directory);
                    }
                }
            }
            else if (chunkType == PngChunkType.tIME)
            {
                var reader    = new SequentialByteArrayReader(bytes);
                var year      = reader.GetUInt16();
                var month     = reader.GetByte();
                int day       = reader.GetByte();
                int hour      = reader.GetByte();
                int minute    = reader.GetByte();
                int second    = reader.GetByte();
                var directory = new PngDirectory(PngChunkType.tIME);
                if (DateUtil.IsValidDate(year, month, day) && DateUtil.IsValidTime(hour, minute, second))
                {
                    var time = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified);
                    directory.Set(PngDirectory.TagLastModificationTime, time);
                }
                else
                {
                    directory.AddError($"PNG tIME data describes an invalid date/time: year={year} month={month} day={day} hour={hour} minute={minute} second={second}");
                }
                yield return(directory);
            }
            else if (chunkType == PngChunkType.pHYs)
            {
                var reader         = new SequentialByteArrayReader(bytes);
                var pixelsPerUnitX = reader.GetInt32();
                var pixelsPerUnitY = reader.GetInt32();
                var unitSpecifier  = reader.GetSByte();
                var directory      = new PngDirectory(PngChunkType.pHYs);
                directory.Set(PngDirectory.TagPixelsPerUnitX, pixelsPerUnitX);
                directory.Set(PngDirectory.TagPixelsPerUnitY, pixelsPerUnitY);
                directory.Set(PngDirectory.TagUnitSpecifier, unitSpecifier);
                yield return(directory);
            }
            else if (chunkType.Equals(PngChunkType.sBIT))
            {
                var directory = new PngDirectory(PngChunkType.sBIT);
                directory.Set(PngDirectory.TagSignificantBits, bytes);
                yield return(directory);
            }
        }
Exemple #23
0
        protected override void Populate(WavFactDirectory directory, byte[] payload)
        {
            var reader = new SequentialByteArrayReader(payload, isMotorolaByteOrder: false);

            directory.Set(TagSampleLength, reader.GetUInt32());
        }