/// <summary>Processes a RIFF data sequence.</summary> /// <param name="reader"> /// the /// <see cref="Com.Drew.Lang.SequentialReader"/> /// from which the data should be read /// </param> /// <param name="handler"> /// the /// <see cref="RiffHandler"/> /// that will coordinate processing and accept read values /// </param> /// <exception cref="RiffProcessingException"> /// if an error occurred during the processing of RIFF data that could not be /// ignored or recovered from /// </exception> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> /// <exception cref="Com.Drew.Imaging.Riff.RiffProcessingException"/> public virtual void ProcessRiff([NotNull] SequentialReader reader, [NotNull] RiffHandler handler) { // RIFF files are always little-endian reader.SetMotorolaByteOrder(false); // PROCESS FILE HEADER string fileFourCC = reader.GetString(4); if (!fileFourCC.Equals("RIFF")) { throw new RiffProcessingException("Invalid RIFF header: " + fileFourCC); } // The total size of the chunks that follow plus 4 bytes for the 'WEBP' FourCC int fileSize = reader.GetInt32(); int sizeLeft = fileSize; string identifier = reader.GetString(4); sizeLeft -= 4; if (!handler.ShouldAcceptRiffIdentifier(identifier)) { return; } // PROCESS CHUNKS while (sizeLeft != 0) { string chunkFourCC = reader.GetString(4); int chunkSize = reader.GetInt32(); sizeLeft -= 8; // NOTE we fail a negative chunk size here (greater than 0x7FFFFFFF) as Java cannot // allocate arrays larger than this. if (chunkSize < 0 || sizeLeft < chunkSize) { throw new RiffProcessingException("Invalid RIFF chunk size"); } if (handler.ShouldAcceptChunk(chunkFourCC)) { // TODO is it feasible to avoid copying the chunk here, and to pass the sequential reader to the handler? handler.ProcessChunk(chunkFourCC, reader.GetBytes(chunkSize)); } else { reader.Skip(chunkSize); } sizeLeft -= chunkSize; // Skip any padding byte added to keep chunks aligned to even numbers of bytes if (chunkSize % 2 == 1) { reader.GetInt8(); sizeLeft--; } } }
/// <summary>Processes a RIFF data sequence.</summary> /// <param name="reader">The <see cref="SequentialReader"/> from which the data should be read.</param> /// <param name="handler">The <see cref="IRiffHandler"/> that will coordinate processing and accept read values.</param> /// <exception cref="RiffProcessingException">An error occurred during the processing of RIFF data that could not be ignored or recovered from.</exception> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> public void ProcessRiff([NotNull] SequentialReader reader, [NotNull] IRiffHandler handler) { // RIFF files are always little-endian reader = reader.WithByteOrder(isMotorolaByteOrder: false); // PROCESS FILE HEADER var fileFourCc = reader.GetString(4, Encoding.ASCII); if (fileFourCc != "RIFF") { throw new RiffProcessingException("Invalid RIFF header: " + fileFourCc); } // The total size of the chunks that follow plus 4 bytes for the 'WEBP' or 'AVI ' FourCC int fileSize = reader.GetInt32(); int sizeLeft = fileSize; string identifier = reader.GetString(4, Encoding.ASCII); sizeLeft -= 4; if (!handler.ShouldAcceptRiffIdentifier(identifier)) { return; } ProcessChunks(reader, sizeLeft, handler); }
// PROCESS CHUNKS private static void ProcessChunks(SequentialReader reader, long maxPosition, IRiffHandler handler) { // Processing chunks. Each chunk is 8 bytes header (4 bytes CC code + 4 bytes length of chunk) + data of the chunk while (reader.Position < maxPosition - 8) { string chunkFourCc = reader.GetString(4, Encoding.ASCII); int chunkSize = reader.GetInt32(); // NOTE we fail a negative chunk size here (greater than 0x7FFFFFFF) as we cannot allocate arrays larger than this if (chunkSize < 0 || chunkSize + reader.Position > maxPosition) { throw new RiffProcessingException("Invalid RIFF chunk size"); } if (chunkFourCc == "LIST" || chunkFourCc == "RIFF") { string listName = reader.GetString(4, Encoding.ASCII); if (handler.ShouldAcceptList(listName)) { ProcessChunks(reader, reader.Position + chunkSize - 4, handler); } else { reader.Skip(chunkSize - 4); } } else { if (handler.ShouldAcceptChunk(chunkFourCc)) { // TODO is it feasible to avoid copying the chunk here, and to pass the sequential reader to the handler? handler.ProcessChunk(chunkFourCc, reader.GetBytes(chunkSize)); } else { reader.Skip(chunkSize); } // Skip any padding byte added to keep chunks aligned to even numbers of bytes if (chunkSize % 2 == 1) { reader.Skip(1); } } } }
static TgaTagInfo GetTag(SequentialReader reader) { return(new TgaTagInfo(reader.GetInt16(), reader.GetInt32(), reader.GetInt32())); }
public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata) { PsdHeaderDirectory directory = new PsdHeaderDirectory(); metadata.AddDirectory(directory); // FILE HEADER SECTION try { int signature = reader.GetInt32(); if (signature != unchecked ((int)(0x38425053))) { // "8BPS" directory.AddError("Invalid PSD file signature"); return; } int version = reader.GetUInt16(); if (version != 1 && version != 2) { directory.AddError("Invalid PSD file version (must be 1 or 2)"); return; } // 6 reserved bytes are skipped here. They should be zero. reader.Skip(6); int channelCount = reader.GetUInt16(); directory.SetInt(PsdHeaderDirectory.TagChannelCount, channelCount); // even though this is probably an unsigned int, the max height in practice is 300,000 int imageHeight = reader.GetInt32(); directory.SetInt(PsdHeaderDirectory.TagImageHeight, imageHeight); // even though this is probably an unsigned int, the max width in practice is 300,000 int imageWidth = reader.GetInt32(); directory.SetInt(PsdHeaderDirectory.TagImageWidth, imageWidth); int bitsPerChannel = reader.GetUInt16(); directory.SetInt(PsdHeaderDirectory.TagBitsPerChannel, bitsPerChannel); int colorMode = reader.GetUInt16(); directory.SetInt(PsdHeaderDirectory.TagColorMode, colorMode); } catch (IOException) { directory.AddError("Unable to read PSD header"); return; } // COLOR MODE DATA SECTION try { long sectionLength = reader.GetUInt32(); /* * Only indexed color and duotone (see the mode field in the File header section) have color mode data. * For all other modes, this section is just the 4-byte length field, which is set to zero. * * Indexed color images: length is 768; color data contains the color table for the image, * in non-interleaved order. * Duotone images: color data contains the duotone specification (the format of which is not documented). * Other applications that read Photoshop files can treat a duotone image as a gray image, * and just preserve the contents of the duotone information when reading and writing the * file. */ reader.Skip(sectionLength); } catch (IOException) { return; } // IMAGE RESOURCES SECTION try { long sectionLength = reader.GetUInt32(); System.Diagnostics.Debug.Assert((sectionLength <= int.MaxValue)); new PhotoshopReader().Extract(reader, (int)sectionLength, metadata); } catch (IOException) { } }
public IEnumerable <PngChunk> Extract([NotNull] SequentialReader reader, [CanBeNull] ICollection <PngChunkType> desiredChunkTypes) { // // PNG DATA STREAM // // Starts with a PNG SIGNATURE, followed by a sequence of CHUNKS. // // PNG SIGNATURE // // Always composed of these bytes: 89 50 4E 47 0D 0A 1A 0A // // CHUNK // // 4 - length of the data field (unsigned, but always within 31 bytes), may be zero // 4 - chunk type, restricted to [65,90] and [97,122] (A-Za-z) // * - data field // 4 - CRC calculated from chunk type and chunk data, but not length // // CHUNK TYPES // // Critical Chunk Types: // // IHDR - image header, always the first chunk in the data stream // PLTE - palette table, associated with indexed PNG images // IDAT - image data chunk, of which there may be many // IEND - image trailer, always the last chunk in the data stream // // Ancillary Chunk Types: // // Transparency information: tRNS // Colour space information: cHRM, gAMA, iCCP, sBIT, sRGB // Textual information: iTXt, tEXt, zTXt // Miscellaneous information: bKGD, hIST, pHYs, sPLT // Time information: tIME // // network byte order reader = reader.WithByteOrder(isMotorolaByteOrder: true); if (!_pngSignatureBytes.SequenceEqual(reader.GetBytes(_pngSignatureBytes.Length))) { throw new PngProcessingException("PNG signature mismatch"); } var seenImageHeader = false; var seenImageTrailer = false; var chunks = new List <PngChunk>(); var seenChunkTypes = new HashSet <PngChunkType>(); while (!seenImageTrailer) { // Process the next chunk. var chunkDataLength = reader.GetInt32(); if (chunkDataLength < 0) { throw new PngProcessingException("PNG chunk length exceeds maximum"); } var chunkType = new PngChunkType(reader.GetBytes(4)); var willStoreChunk = desiredChunkTypes == null || desiredChunkTypes.Contains(chunkType); var chunkData = reader.GetBytes(chunkDataLength); // Skip the CRC bytes at the end of the chunk // TODO consider verifying the CRC value to determine if we're processing bad data reader.Skip(4); if (willStoreChunk && seenChunkTypes.Contains(chunkType) && !chunkType.AreMultipleAllowed) { throw new PngProcessingException($"Observed multiple instances of PNG chunk '{chunkType}', for which multiples are not allowed"); } if (chunkType.Equals(PngChunkType.IHDR)) { seenImageHeader = true; } else if (!seenImageHeader) { throw new PngProcessingException($"First chunk should be '{PngChunkType.IHDR}', but '{chunkType}' was observed"); } if (chunkType.Equals(PngChunkType.IEND)) { seenImageTrailer = true; } if (willStoreChunk) { chunks.Add(new PngChunk(chunkType, chunkData)); } seenChunkTypes.Add(chunkType); } return(chunks); }
/// <summary>Processes a RIFF data sequence.</summary> /// <param name="reader">The <see cref="SequentialReader"/> from which the data should be read.</param> /// <param name="handler">The <see cref="IRiffHandler"/> that will coordinate processing and accept read values.</param> /// <exception cref="RiffProcessingException">An error occurred during the processing of RIFF data that could not be ignored or recovered from.</exception> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> public void ProcessRiff([NotNull] SequentialReader reader, [NotNull] IRiffHandler handler) { // RIFF files are always little-endian reader = reader.WithByteOrder(isMotorolaByteOrder: false); // PROCESS FILE HEADER var fileFourCc = reader.GetString(4, Encoding.UTF8); if (fileFourCc != "RIFF") { throw new RiffProcessingException("Invalid RIFF header: " + fileFourCc); } // The total size of the chunks that follow plus 4 bytes for the 'WEBP' FourCC var fileSize = reader.GetInt32(); var sizeLeft = fileSize; var identifier = reader.GetString(4, Encoding.UTF8); sizeLeft -= 4; if (!handler.ShouldAcceptRiffIdentifier(identifier)) { return; } // PROCESS CHUNKS while (sizeLeft != 0) { var chunkFourCc = reader.GetString(4, Encoding.UTF8); var chunkSize = reader.GetInt32(); sizeLeft -= 8; // NOTE we fail a negative chunk size here (greater than 0x7FFFFFFF) as we cannot allocate arrays larger than this if (chunkSize < 0 || sizeLeft < chunkSize) { throw new RiffProcessingException("Invalid RIFF chunk size"); } if (handler.ShouldAcceptChunk(chunkFourCc)) { // TODO is it feasible to avoid copying the chunk here, and to pass the sequential reader to the handler? handler.ProcessChunk(chunkFourCc, reader.GetBytes(chunkSize)); } else { reader.Skip(chunkSize); } sizeLeft -= chunkSize; // Skip any padding byte added to keep chunks aligned to even numbers of bytes if (chunkSize % 2 == 1) { reader.GetSByte(); sizeLeft--; } } }
Extract([NotNull] SequentialReader reader, int length) { var directory = new PhotoshopDirectory(); var directories = new List <Directory> { directory }; // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature "8BIM" // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 var pos = 0; while (pos < length) { try { // 4 bytes for the signature. Should always be "8BIM". var signature = reader.GetString(4); pos += 4; if (signature != "8BIM") { throw new ImageProcessingException("Expecting 8BIM marker"); } // 2 bytes for the resource identifier (tag type). var tagType = reader.GetUInt16(); pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). var descriptionLength = reader.GetByte(); pos += 1; // Some basic bounds checking if (descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // We don't use the string value here reader.Skip(descriptionLength); pos += descriptionLength; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. var byteCount = reader.GetInt32(); pos += 4; // The resource data. var tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } switch (tagType) { case PhotoshopDirectory.TagIptc: directories.Add(new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length)); break; case PhotoshopDirectory.TagIccProfileBytes: directories.Add(new IccReader().Extract(new ByteArrayReader(tagBytes))); break; case PhotoshopDirectory.TagExifData1: case PhotoshopDirectory.TagExifData3: directories.AddRange(new ExifReader().Extract(new ByteArrayReader(tagBytes))); break; case PhotoshopDirectory.TagXmpData: directories.Add(new XmpReader().Extract(tagBytes)); break; default: directory.Set(tagType, tagBytes); break; } if (tagType >= 0x0fa0 && tagType <= 0x1387) { PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data"; } } catch (Exception ex) { directory.AddError(ex.Message); break; } } return(directories); }
public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata) { BmpHeaderDirectory directory = metadata.GetOrCreateDirectory <BmpHeaderDirectory>(); // FILE HEADER // // 2 - magic number (0x42 0x4D = "BM") // 4 - size of BMP file in bytes // 2 - reserved // 2 - reserved // 4 - the offset of the pixel array // // BITMAP INFORMATION HEADER // // The first four bytes of the header give the size, which is a discriminator of the actual header format. // See this for more information http://en.wikipedia.org/wiki/BMP_file_format // // BITMAPINFOHEADER (size = 40) // // 4 - size of header // 4 - pixel width (signed) // 4 - pixel height (signed) // 2 - number of colour planes (must be set to 1) // 2 - number of bits per pixel // 4 - compression being used (needs decoding) // 4 - pixel data length (not total file size, just pixel array) // 4 - horizontal resolution, pixels/meter (signed) // 4 - vertical resolution, pixels/meter (signed) // 4 - number of colours in the palette (0 means no palette) // 4 - number of important colours (generally ignored) // // BITMAPCOREHEADER (size = 12) // // 4 - size of header // 2 - pixel width // 2 - pixel height // 2 - number of colour planes (must be set to 1) // 2 - number of bits per pixel // // COMPRESSION VALUES // // 0 = None // 1 = RLE 8-bit/pixel // 2 = RLE 4-bit/pixel // 3 = Bit field (or Huffman 1D if BITMAPCOREHEADER2 (size 64)) // 4 = JPEG (or RLE-24 if BITMAPCOREHEADER2 (size 64)) // 5 = PNG // 6 = Bit field reader.SetMotorolaByteOrder(false); try { int magicNumber = reader.GetUInt16(); if (magicNumber != unchecked ((int)(0x4D42))) { directory.AddError("Invalid BMP magic number"); return; } // skip past the rest of the file header reader.Skip(4 + 2 + 2 + 4); int headerSize = reader.GetInt32(); directory.SetInt(BmpHeaderDirectory.TagHeaderSize, headerSize); // We expect the header size to be either 40 (BITMAPINFOHEADER) or 12 (BITMAPCOREHEADER) if (headerSize == 40) { // BITMAPINFOHEADER directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt32()); directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt32()); directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16()); directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16()); directory.SetInt(BmpHeaderDirectory.TagCompression, reader.GetInt32()); // skip the pixel data length reader.Skip(4); directory.SetInt(BmpHeaderDirectory.TagXPixelsPerMeter, reader.GetInt32()); directory.SetInt(BmpHeaderDirectory.TagYPixelsPerMeter, reader.GetInt32()); directory.SetInt(BmpHeaderDirectory.TagPaletteColourCount, reader.GetInt32()); directory.SetInt(BmpHeaderDirectory.TagImportantColourCount, reader.GetInt32()); } else { if (headerSize == 12) { directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt16()); directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt16()); directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16()); directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16()); } else { directory.AddError("Unexpected DIB header size: " + headerSize); } } } catch (IOException) { directory.AddError("Unable to read BMP header"); } }
/// <exception cref="Com.Drew.Imaging.Png.PngProcessingException"/> /// <exception cref="System.IO.IOException"/> public virtual Iterable <PngChunk> Extract(SequentialReader reader, ICollection <PngChunkType> desiredChunkTypes) { // // PNG DATA STREAM // // Starts with a PNG SIGNATURE, followed by a sequence of CHUNKS. // // PNG SIGNATURE // // Always composed of these bytes: 89 50 4E 47 0D 0A 1A 0A // // CHUNK // // 4 - length of the data field (unsigned, but always within 31 bytes), may be zero // 4 - chunk type, restricted to [65,90] and [97,122] (A-Za-z) // * - data field // 4 - CRC calculated from chunk type and chunk data, but not length // // CHUNK TYPES // // Critical Chunk Types: // // IHDR - image header, always the first chunk in the data stream // PLTE - palette table, associated with indexed PNG images // IDAT - image data chunk, of which there may be many // IEND - image trailer, always the last chunk in the data stream // // Ancillary Chunk Types: // // Transparency information: tRNS // Colour space information: cHRM, gAMA, iCCP, sBIT, sRGB // Textual information: iTXt, tEXt, zTXt // Miscellaneous information: bKGD, hIST, pHYs, sPLT // Time information: tIME // reader.SetMotorolaByteOrder(true); // network byte order if (!Arrays.Equals(PngSignatureBytes, reader.GetBytes(PngSignatureBytes.Length))) { throw new PngProcessingException("PNG signature mismatch"); } bool seenImageHeader = false; bool seenImageTrailer = false; IList <PngChunk> chunks = new AList <PngChunk>(); ICollection <PngChunkType> seenChunkTypes = new HashSet <PngChunkType>(); while (!seenImageTrailer) { // Process the next chunk. int chunkDataLength = reader.GetInt32(); PngChunkType chunkType = new PngChunkType(reader.GetBytes(4)); sbyte[] chunkData = reader.GetBytes(chunkDataLength); // Skip the CRC bytes at the end of the chunk // TODO consider verifying the CRC value to determine if we're processing bad data reader.Skip(4); if (seenChunkTypes.Contains(chunkType) && !chunkType.AreMultipleAllowed()) { throw new PngProcessingException(Sharpen.Extensions.StringFormat("Observed multiple instances of PNG chunk '%s', for which multiples are not allowed", chunkType)); } if (chunkType.Equals(PngChunkType.Ihdr)) { seenImageHeader = true; } else { if (!seenImageHeader) { throw new PngProcessingException(Sharpen.Extensions.StringFormat("First chunk should be '%s', but '%s' was observed", PngChunkType.Ihdr, chunkType)); } } if (chunkType.Equals(PngChunkType.Iend)) { seenImageTrailer = true; } if (desiredChunkTypes == null || desiredChunkTypes.Contains(chunkType)) { chunks.Add(new PngChunk(chunkType, chunkData)); } seenChunkTypes.Add(chunkType); } return(chunks.AsIterable()); }
public DirectoryList Extract(SequentialReader reader) { var directory = new PsdHeaderDirectory(); // FILE HEADER SECTION try { var signature = reader.GetInt32(); var version = reader.GetUInt16(); if (signature != 0x38425053) { // "8BPS" directory.AddError("Invalid PSD file signature"); } else if (version != 1 && version != 2) { directory.AddError("Invalid PSD file version (must be 1 or 2)"); } else { // 6 reserved bytes are skipped here. They should be zero. reader.Skip(6); var channelCount = reader.GetUInt16(); directory.Set(PsdHeaderDirectory.TagChannelCount, channelCount); // even though this is probably an unsigned int, the max height in practice is 300,000 var imageHeight = reader.GetInt32(); directory.Set(PsdHeaderDirectory.TagImageHeight, imageHeight); // even though this is probably an unsigned int, the max width in practice is 300,000 var imageWidth = reader.GetInt32(); directory.Set(PsdHeaderDirectory.TagImageWidth, imageWidth); var bitsPerChannel = reader.GetUInt16(); directory.Set(PsdHeaderDirectory.TagBitsPerChannel, bitsPerChannel); var colorMode = reader.GetUInt16(); directory.Set(PsdHeaderDirectory.TagColorMode, colorMode); } } catch (IOException) { directory.AddError("Unable to read PSD header"); } if (directory.HasError) { return new Directory[] { directory } } ; IEnumerable <Directory>?photoshopDirectories = null; try { // COLOR MODE DATA SECTION // Only indexed color and duotone (see the mode field in the File header section) have color mode data. // For all other modes, this section is just the 4-byte length field, which is set to zero. // // Indexed color images: length is 768; color data contains the color table for the image, // in non-interleaved order. // Duotone images: color data contains the duotone specification (the format of which is not documented). // Other applications that read Photoshop files can treat a duotone image as a gray image, // and just preserve the contents of the duotone information when reading and writing the // file. var colorModeSectionLength = reader.GetUInt32(); reader.Skip(colorModeSectionLength); // IMAGE RESOURCES SECTION var imageResourcesSectionLength = reader.GetUInt32(); Debug.Assert(imageResourcesSectionLength <= int.MaxValue); photoshopDirectories = new PhotoshopReader().Extract(reader, (int)imageResourcesSectionLength); } catch (IOException) { } var directories = new List <Directory> { directory }; if (photoshopDirectories != null) { directories.AddRange(photoshopDirectories); } // LAYER AND MASK INFORMATION SECTION (skipped) // IMAGE DATA SECTION (skipped) return(directories); } }
// PROCESS CHUNKS public void ProcessChunks([NotNull] SequentialReader reader, int sizeLeft, [NotNull] IRiffHandler handler) { // Processing chunks. Each chunk is 8 bytes header (4 bytes CC code + 4 bytes length of chunk) + data of the chunk while (reader.Position < sizeLeft) { // Check if end of the file is closer then 8 bytes if (reader.IsCloserToEnd(8)) { return; } string chunkFourCc = reader.GetString(4, Encoding.ASCII); int chunkSize = reader.GetInt32(); sizeLeft -= 8; // NOTE we fail a negative chunk size here (greater than 0x7FFFFFFF) as we cannot allocate arrays larger than this if (chunkSize < 0 || sizeLeft < chunkSize) { throw new RiffProcessingException("Invalid RIFF chunk size"); } // Check if end of the file is closer then chunkSize bytes if (reader.IsCloserToEnd(chunkSize)) { return; } if (chunkFourCc == "LIST" || chunkFourCc == "RIFF") { string listName = reader.GetString(4, Encoding.ASCII); if (handler.ShouldAcceptList(listName)) { ProcessChunks(reader, sizeLeft - 4, handler); } else { reader.Skip(sizeLeft - 4); } sizeLeft -= chunkSize; } else { if (handler.ShouldAcceptChunk(chunkFourCc)) { // TODO is it feasible to avoid copying the chunk here, and to pass the sequential reader to the handler? handler.ProcessChunk(chunkFourCc, reader.GetBytes(chunkSize)); } else { reader.Skip(chunkSize); } sizeLeft -= chunkSize; // Skip any padding byte added to keep chunks aligned to even numbers of bytes if (chunkSize % 2 == 1) { reader.GetSByte(); sizeLeft--; } } } }
// http://id3.org/mp3Frame // https://www.loc.gov/preservation/digital/formats/fdd/fdd000105.shtml public Directory Extract(SequentialReader reader) { var directory = new Mp3Directory(); var header = reader.GetInt32(); // ID: MPEG-2.5, MPEG-2, or MPEG-1 int id = 0; switch ((header & 0x000180000) >> 19) { case 0: throw new ImageProcessingException("MPEG-2.5 not supported."); case 2: directory.Set(Mp3Directory.TAG_ID, "MPEG-2"); id = 2; break; case 3: directory.Set(Mp3Directory.TAG_ID, "MPEG-1"); id = 1; break; } // Layer Type: 1, 2, 3, or not defined int layer = (header & 0x00060000) >> 17; switch (layer) { case 0: directory.Set(Mp3Directory.TAG_LAYER, "Not defined"); break; case 1: directory.Set(Mp3Directory.TAG_LAYER, "Layer III"); break; case 2: directory.Set(Mp3Directory.TAG_LAYER, "Layer II"); break; case 3: directory.Set(Mp3Directory.TAG_LAYER, "Layer I"); break; } int protectionBit = (header & 0x00010000) >> 16; // Bitrate: depends on ID and Layer int bitrate = (header & 0x0000F000) >> 12; if (bitrate != 0 && bitrate != 15) { directory.Set(Mp3Directory.TAG_BITRATE, SetBitrate(bitrate, layer, id)); } // Frequency: depends on ID int frequency = (header & 0x00000C00) >> 10; int[,] frequencyMapping = new int[2, 3] { { 44100, 48000, 32000 }, { 22050, 24000, 16000 } }; if (id == 1) { directory.Set(Mp3Directory.TAG_FREQUENCY, frequencyMapping[0, frequency]); frequency = frequencyMapping[0, frequency]; } else if (id == 2) { directory.Set(Mp3Directory.TAG_FREQUENCY, frequencyMapping[1, frequency]); frequency = frequencyMapping[1, frequency]; } int paddingBit = (header & 0x00000200) >> 9; // Encoding type: Stereo, Joint Stereo, Dual Channel, or Mono int mode = (header & 0x000000C0) >> 6; switch (mode) { case 0: directory.Set(Mp3Directory.TAG_MODE, "Stereo"); break; case 1: directory.Set(Mp3Directory.TAG_MODE, "Joint stereo"); break; case 2: directory.Set(Mp3Directory.TAG_MODE, "Dual channel"); break; case 3: directory.Set(Mp3Directory.TAG_MODE, "Mono"); break; } // Copyright boolean int copyright = (header & 0x00000008) >> 3; switch (copyright) { case 0: directory.Set(Mp3Directory.TAG_COPYRIGHT, "False"); break; case 1: directory.Set(Mp3Directory.TAG_COPYRIGHT, "True"); break; } int emphasis = header & 0x00000003; switch (emphasis) { case 0: directory.Set(Mp3Directory.TAG_EMPHASIS, "none"); break; case 1: directory.Set(Mp3Directory.TAG_EMPHASIS, "50/15ms"); break; case 3: directory.Set(Mp3Directory.TAG_EMPHASIS, "CCITT j.17"); break; } int frameSize = SetBitrate(bitrate, layer, id) * 1000 * 144 / frequency; directory.Set(Mp3Directory.TAG_FRAME_SIZE, frameSize + " bytes"); return(directory); }
public virtual void Extract([NotNull] SequentialReader reader, int length, [NotNull] Com.Drew.Metadata.Metadata metadata) { PhotoshopDirectory directory = new PhotoshopDirectory(); metadata.AddDirectory(directory); // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature "8BIM" // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 int pos = 0; while (pos < length) { try { // 4 bytes for the signature. Should always be "8BIM". string signature = reader.GetString(4); if (!signature.Equals("8BIM")) { throw new ImageProcessingException("Expecting 8BIM marker"); } pos += 4; // 2 bytes for the resource identifier (tag type). int tagType = reader.GetUInt16(); // segment type pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). short descriptionLength = reader.GetUInt8(); pos += 1; // Some basic bounds checking if (descriptionLength < 0 || descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // We don't use the string value here reader.Skip(descriptionLength); pos += descriptionLength; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. int byteCount = reader.GetInt32(); pos += 4; // The resource data. sbyte[] tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } if (tagType == PhotoshopDirectory.TagIptc) { new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.Length); } else { if (tagType == PhotoshopDirectory.TagIccProfileBytes) { new IccReader().Extract(new ByteArrayReader(tagBytes), metadata); } else { if (tagType == PhotoshopDirectory.TagExifData1 || tagType == PhotoshopDirectory.TagExifData3) { new ExifReader().Extract(new ByteArrayReader(tagBytes), metadata); } else { if (tagType == PhotoshopDirectory.TagXmpData) { new XmpReader().Extract(tagBytes, metadata); } else { directory.SetByteArray(tagType, tagBytes); } } } } if (tagType >= unchecked ((int)(0x0fa0)) && tagType <= unchecked ((int)(0x1387))) { PhotoshopDirectory._tagNameMap.Put(tagType, Sharpen.Extensions.StringFormat("Plug-in %d Data", tagType - unchecked ((int)(0x0fa0)) + 1)); } } catch (Exception ex) { directory.AddError(ex.Message); return; } } }
public DirectoryList Extract(SequentialReader reader, int length) { var directory = new PhotoshopDirectory(); var directories = new List <Directory> { directory }; // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 var pos = 0; while (pos < length) { try { // 4 bytes for the signature ("8BIM", "PHUT", etc.) var signature = reader.GetString(4, Encoding.UTF8); pos += 4; // 2 bytes for the resource identifier (tag type). var tagType = reader.GetUInt16(); pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). var descriptionLength = reader.GetByte(); pos += 1; // Some basic bounds checking if (descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // We don't use the string value here reader.Skip(descriptionLength); pos += descriptionLength; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. var byteCount = reader.GetInt32(); pos += 4; // The resource data. var tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // Skip any unsupported IRBs if (signature != "8BIM") { continue; } switch (tagType) { case PhotoshopDirectory.TagIptc: var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length); iptcDirectory.Parent = directory; directories.Add(iptcDirectory); break; case PhotoshopDirectory.TagIccProfileBytes: var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes)); iccDirectory.Parent = directory; directories.Add(iccDirectory); break; case PhotoshopDirectory.TagExifData1: case PhotoshopDirectory.TagExifData3: var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes)); foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null)) { exifDirectory.Parent = directory; } directories.AddRange(exifDirectories); break; case PhotoshopDirectory.TagXmpData: var xmpDirectory = new XmpReader().Extract(tagBytes); xmpDirectory.Parent = directory; directories.Add(xmpDirectory); break; default: directory.Set(tagType, tagBytes); break; } if (tagType >= 0x0fa0 && tagType <= 0x1387) { PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data"; } } catch (Exception ex) { directory.AddError(ex.Message); break; } } return(directories); }
public DirectoryList Extract(SequentialReader reader, int length) { var directory = new PhotoshopDirectory(); var directories = new List <Directory> { directory }; // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 var pos = 0; int clippingPathCount = 0; while (pos < length) { try { // 4 bytes for the signature ("8BIM", "PHUT", etc.) var signature = reader.GetString(4, Encoding.UTF8); pos += 4; // 2 bytes for the resource identifier (tag type). var tagType = reader.GetUInt16(); pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). var descriptionLength = reader.GetByte(); pos += 1; // Some basic bounds checking if (descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // Get name (important for paths) var description = new StringBuilder(); // Loop through each byte and append to string while (descriptionLength > 0) { description.Append((char)reader.GetByte()); pos++; descriptionLength--; } // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. var byteCount = reader.GetInt32(); pos += 4; // The resource data. var tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // Skip any unsupported IRBs if (signature != "8BIM") { continue; } switch (tagType) { case PhotoshopDirectory.TagIptc: var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length); iptcDirectory.Parent = directory; directories.Add(iptcDirectory); break; case PhotoshopDirectory.TagIccProfileBytes: var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes)); iccDirectory.Parent = directory; directories.Add(iccDirectory); break; case PhotoshopDirectory.TagExifData1: case PhotoshopDirectory.TagExifData3: var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes)); foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null)) { exifDirectory.Parent = directory; } directories.AddRange(exifDirectories); break; case PhotoshopDirectory.TagXmpData: var xmpDirectory = new XmpReader().Extract(tagBytes); xmpDirectory.Parent = directory; directories.Add(xmpDirectory); break; default: if (tagType >= PhotoshopDirectory.TagClippingPathBlockStart && tagType <= PhotoshopDirectory.TagClippingPathBlockEnd) { clippingPathCount++; Array.Resize(ref tagBytes, tagBytes.Length + description.Length + 1); // Append description(name) to end of byte array with 1 byte before the description representing the length for (int i = tagBytes.Length - description.Length - 1; i < tagBytes.Length; i++) { if (i % (tagBytes.Length - description.Length - 1 + description.Length) == 0) { tagBytes[i] = (byte)description.Length; } else { tagBytes[i] = (byte)description[i - (tagBytes.Length - description.Length - 1)]; } } PhotoshopDirectory.TagNameMap[PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1] = "Path Info " + clippingPathCount; directory.Set(PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1, tagBytes); } else { directory.Set(tagType, tagBytes); } break; } if (tagType >= 0x0fa0 && tagType <= 0x1387) { PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data"; } } catch (Exception ex) { directory.AddError(ex.Message); break; } } return(directories); }
private static void ReadBitmapHeader(SequentialReader reader, BmpHeaderDirectory directory, List <Directory> directories) { /* * BITMAPCOREHEADER (12 bytes): * * DWORD Size - Size of this header in bytes * SHORT Width - Image width in pixels * SHORT Height - Image height in pixels * WORD Planes - Number of color planes * WORD BitsPerPixel - Number of bits per pixel * * OS21XBITMAPHEADER (12 bytes): * * DWORD Size - Size of this structure in bytes * WORD Width - Bitmap width in pixels * WORD Height - Bitmap height in pixel * WORD NumPlanes - Number of bit planes (color depth) * WORD BitsPerPixel - Number of bits per pixel per plane * * OS22XBITMAPHEADER (16/64 bytes): * * DWORD Size - Size of this structure in bytes * DWORD Width - Bitmap width in pixels * DWORD Height - Bitmap height in pixel * WORD NumPlanes - Number of bit planes (color depth) * WORD BitsPerPixel - Number of bits per pixel per plane * * - Short version ends here - * * DWORD Compression - Bitmap compression scheme * DWORD ImageDataSize - Size of bitmap data in bytes * DWORD XResolution - X resolution of display device * DWORD YResolution - Y resolution of display device * DWORD ColorsUsed - Number of color table indices used * DWORD ColorsImportant - Number of important color indices * WORD Units - Type of units used to measure resolution * WORD Reserved - Pad structure to 4-byte boundary * WORD Recording - Recording algorithm * WORD Rendering - Halftoning algorithm used * DWORD Size1 - Reserved for halftoning algorithm use * DWORD Size2 - Reserved for halftoning algorithm use * DWORD ColorEncoding - Color model used in bitmap * DWORD Identifier - Reserved for application use * * BITMAPINFOHEADER (40 bytes), BITMAPV2INFOHEADER (52 bytes), BITMAPV3INFOHEADER (56 bytes), * BITMAPV4HEADER (108 bytes) and BITMAPV5HEADER (124 bytes): * * DWORD Size - Size of this header in bytes * LONG Width - Image width in pixels * LONG Height - Image height in pixels * WORD Planes - Number of color planes * WORD BitsPerPixel - Number of bits per pixel * DWORD Compression - Compression methods used * DWORD SizeOfBitmap - Size of bitmap in bytes * LONG HorzResolution - Horizontal resolution in pixels per meter * LONG VertResolution - Vertical resolution in pixels per meter * DWORD ColorsUsed - Number of colors in the image * DWORD ColorsImportant - Minimum number of important colors * * - BITMAPINFOHEADER ends here - * * DWORD RedMask - Mask identifying bits of red component * DWORD GreenMask - Mask identifying bits of green component * DWORD BlueMask - Mask identifying bits of blue component * * - BITMAPV2INFOHEADER ends here - * * DWORD AlphaMask - Mask identifying bits of alpha component * * - BITMAPV3INFOHEADER ends here - * * DWORD CSType - Color space type * LONG RedX - X coordinate of red endpoint * LONG RedY - Y coordinate of red endpoint * LONG RedZ - Z coordinate of red endpoint * LONG GreenX - X coordinate of green endpoint * LONG GreenY - Y coordinate of green endpoint * LONG GreenZ - Z coordinate of green endpoint * LONG BlueX - X coordinate of blue endpoint * LONG BlueY - Y coordinate of blue endpoint * LONG BlueZ - Z coordinate of blue endpoint * DWORD GammaRed - Gamma red coordinate scale value * DWORD GammaGreen - Gamma green coordinate scale value * DWORD GammaBlue - Gamma blue coordinate scale value * * - BITMAPV4HEADER ends here - * * DWORD Intent - Rendering intent for bitmap * DWORD ProfileData - Offset of the profile data relative to BITMAPV5HEADER * DWORD ProfileSize - Size, in bytes, of embedded profile data * DWORD Reserved - Shall be zero * */ try { int bitmapType = directory.GetInt32(BmpHeaderDirectory.TagBitmapType); long headerOffset = reader.Position; int headerSize = reader.GetInt32(); directory.Set(BmpHeaderDirectory.TagHeaderSize, headerSize); /* * Known header type sizes: * * 12 - BITMAPCOREHEADER or OS21XBITMAPHEADER * 16 - OS22XBITMAPHEADER (short) * 40 - BITMAPINFOHEADER * 52 - BITMAPV2INFOHEADER * 56 - BITMAPV3INFOHEADER * 64 - OS22XBITMAPHEADER (full) * 108 - BITMAPV4HEADER * 124 - BITMAPV5HEADER * */ if (headerSize == 12 && bitmapType == (int)BmpHeaderDirectory.BitmapType.Bitmap) { //BITMAPCOREHEADER /* * There's no way to tell BITMAPCOREHEADER and OS21XBITMAPHEADER * apart for the "standard" bitmap type. The difference is only * that BITMAPCOREHEADER has signed width and height while * in OS21XBITMAPHEADER they are unsigned. Since BITMAPCOREHEADER, * the Windows version, is most common, read them as signed. */ directory.Set(BmpHeaderDirectory.TagImageWidth, reader.GetInt16()); directory.Set(BmpHeaderDirectory.TagImageHeight, reader.GetInt16()); directory.Set(BmpHeaderDirectory.TagColourPlanes, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagBitsPerPixel, reader.GetUInt16()); } else if (headerSize == 12) { // OS21XBITMAPHEADER directory.Set(BmpHeaderDirectory.TagImageWidth, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagImageHeight, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagColourPlanes, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagBitsPerPixel, reader.GetUInt16()); } else if (headerSize == 16 || headerSize == 64) { // OS22XBITMAPHEADER directory.Set(BmpHeaderDirectory.TagImageWidth, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagImageHeight, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagColourPlanes, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagBitsPerPixel, reader.GetUInt16()); if (headerSize > 16) { directory.Set(BmpHeaderDirectory.TagCompression, reader.GetInt32()); reader.Skip(4); // skip the pixel data length directory.Set(BmpHeaderDirectory.TagXPixelsPerMeter, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagYPixelsPerMeter, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagPaletteColourCount, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagImportantColourCount, reader.GetInt32()); reader.Skip( 2 + // Skip Units, can only be 0 (pixels per meter) 2 + // Skip padding 2 // Skip Recording, can only be 0 (left to right, bottom to top) ); directory.Set(BmpHeaderDirectory.TagRendering, reader.GetUInt16()); reader.Skip(4 + 4); // Skip Size1 and Size2 directory.Set(BmpHeaderDirectory.TagColorEncoding, reader.GetInt32()); reader.Skip(4); // Skip Identifier } } else if ( headerSize == 40 || headerSize == 52 || headerSize == 56 || headerSize == 108 || headerSize == 124) { // BITMAPINFOHEADER V1-5 directory.Set(BmpHeaderDirectory.TagImageWidth, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagImageHeight, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagColourPlanes, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagBitsPerPixel, reader.GetUInt16()); directory.Set(BmpHeaderDirectory.TagCompression, reader.GetInt32()); // skip the pixel data length reader.Skip(4); directory.Set(BmpHeaderDirectory.TagXPixelsPerMeter, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagYPixelsPerMeter, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagPaletteColourCount, reader.GetInt32()); directory.Set(BmpHeaderDirectory.TagImportantColourCount, reader.GetInt32()); if (headerSize == 40) { // BITMAPINFOHEADER end return; } directory.Set(BmpHeaderDirectory.TagRedMask, reader.GetUInt32()); directory.Set(BmpHeaderDirectory.TagGreenMask, reader.GetUInt32()); directory.Set(BmpHeaderDirectory.TagBlueMask, reader.GetUInt32()); if (headerSize == 52) { // BITMAPV2INFOHEADER end return; } directory.Set(BmpHeaderDirectory.TagAlphaMask, reader.GetUInt32()); if (headerSize == 56) { // BITMAPV3INFOHEADER end return; } long csType = reader.GetUInt32(); directory.Set(BmpHeaderDirectory.TagColorSpaceType, csType); reader.Skip(36); // Skip color endpoint coordinates directory.Set(BmpHeaderDirectory.TagGammaRed, reader.GetUInt32()); directory.Set(BmpHeaderDirectory.TagGammaGreen, reader.GetUInt32()); directory.Set(BmpHeaderDirectory.TagGammaBlue, reader.GetUInt32()); if (headerSize == 108) { // BITMAPV4HEADER end return; } directory.Set(BmpHeaderDirectory.TagIntent, reader.GetInt32()); if (csType == (long)BmpHeaderDirectory.ColorSpaceType.ProfileEmbedded || csType == (long)BmpHeaderDirectory.ColorSpaceType.ProfileLinked) { long profileOffset = reader.GetUInt32(); int profileSize = reader.GetInt32(); if (reader.Position > headerOffset + profileOffset) { directory.AddError("Invalid profile data offset 0x" + (headerOffset + profileOffset).ToString("X8")); return; } reader.Skip(headerOffset + profileOffset - reader.Position); if (csType == (long)BmpHeaderDirectory.ColorSpaceType.ProfileLinked) { directory.Set(BmpHeaderDirectory.TagLinkedProfile, reader.GetNullTerminatedString(profileSize, Encoding.GetEncoding(1252))); } else { var iccReader = new ByteArrayReader(reader.GetBytes(profileSize)); var iccDirectory = new IccReader().Extract(iccReader); iccDirectory.Parent = directory; directories.Add(iccDirectory); } } else { reader.Skip( 4 + // Skip ProfileData offset 4 + // Skip ProfileSize 4 // Skip Reserved ); } } else { directory.AddError("Unexpected DIB header size: " + headerSize); } } catch (IOException) { directory.AddError("Unable to read BMP header"); } catch (MetadataException) { directory.AddError("Internal error"); } }