public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType) { JpegCommentDirectory directory = metadata.GetOrCreateDirectory <JpegCommentDirectory>(); // The entire contents of the directory are the comment directory.SetString(JpegCommentDirectory.TagComment, Sharpen.Runtime.GetStringForBytes(segmentBytes)); }
public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata) { // TODO review whether the 'tagPtr' values below really do require ICC processing to work with a RandomAccessReader IccDirectory directory = metadata.GetOrCreateDirectory <IccDirectory>(); try { directory.SetInt(IccDirectory.TagProfileByteCount, reader.GetInt32(IccDirectory.TagProfileByteCount)); // For these tags, the int value of the tag is in fact it's offset within the buffer. Set4ByteString(directory, IccDirectory.TagCmmType, reader); SetInt32(directory, IccDirectory.TagProfileVersion, reader); Set4ByteString(directory, IccDirectory.TagProfileClass, reader); Set4ByteString(directory, IccDirectory.TagColorSpace, reader); Set4ByteString(directory, IccDirectory.TagProfileConnectionSpace, reader); SetDate(directory, IccDirectory.TagProfileDatetime, reader); Set4ByteString(directory, IccDirectory.TagSignature, reader); Set4ByteString(directory, IccDirectory.TagPlatform, reader); SetInt32(directory, IccDirectory.TagCmmFlags, reader); Set4ByteString(directory, IccDirectory.TagDeviceMake, reader); int temp = reader.GetInt32(IccDirectory.TagDeviceModel); if (temp != 0) { if (temp <= unchecked ((int)(0x20202020))) { directory.SetInt(IccDirectory.TagDeviceModel, temp); } else { directory.SetString(IccDirectory.TagDeviceModel, GetStringFromInt32(temp)); } } SetInt32(directory, IccDirectory.TagRenderingIntent, reader); SetInt64(directory, IccDirectory.TagDeviceAttr, reader); float[] xyz = new float[] { reader.GetS15Fixed16(IccDirectory.TagXyzValues), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 4), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 8) }; directory.SetObject(IccDirectory.TagXyzValues, xyz); // Process 'ICC tags' int tagCount = reader.GetInt32(IccDirectory.TagTagCount); directory.SetInt(IccDirectory.TagTagCount, tagCount); for (int i = 0; i < tagCount; i++) { int pos = IccDirectory.TagTagCount + 4 + i * 12; int tagType = reader.GetInt32(pos); int tagPtr = reader.GetInt32(pos + 4); int tagLen = reader.GetInt32(pos + 8); sbyte[] b = reader.GetBytes(tagPtr, tagLen); directory.SetByteArray(tagType, b); } } catch (IOException ex) { directory.AddError("Exception reading ICC profile: " + ex.Message); } }
public virtual void ExtractTiff(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata) { ExifIFD0Directory directory = metadata.GetOrCreateDirectory <ExifIFD0Directory>(); try { ExtractTiff(reader, metadata, directory, 0); } catch (IOException e) { directory.AddError("IO problem: " + e.Message); } }
/// <summary> /// Performs the XMP data extraction, adding found values to the specified instance of /// <see cref="Com.Drew.Metadata.Metadata"/> /// . /// <p/> /// The extraction is done with Adobe's XMPCore library. /// </summary> public virtual void Extract(string xmpString, Com.Drew.Metadata.Metadata metadata) { XmpDirectory directory = metadata.GetOrCreateDirectory <XmpDirectory>(); try { XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString); ProcessXmpTags(directory, xmpMeta); } catch (XMPException e) { directory.AddError("Error processing XMP data: " + e.Message); } }
/// <summary> /// Performs the Jfif data extraction, adding found values to the specified /// instance of /// <see cref="Com.Drew.Metadata.Metadata"/> /// . /// </summary> public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata) { JfifDirectory directory = metadata.GetOrCreateDirectory <JfifDirectory>(); try { // For JFIF, the tag number is also the offset into the segment int ver = reader.GetUInt16(JfifDirectory.TagVersion); directory.SetInt(JfifDirectory.TagVersion, ver); int units = reader.GetUInt8(JfifDirectory.TagUnits); directory.SetInt(JfifDirectory.TagUnits, units); int height = reader.GetUInt16(JfifDirectory.TagResx); directory.SetInt(JfifDirectory.TagResx, height); int width = reader.GetUInt16(JfifDirectory.TagResy); directory.SetInt(JfifDirectory.TagResy, width); } catch (IOException me) { directory.AddError(me.Message); } }
public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata) { Com.Drew.Metadata.Directory directory = metadata.GetOrCreateDirectory <AdobeJpegDirectory>(); try { reader.SetMotorolaByteOrder(false); if (!reader.GetString(5).Equals("Adobe")) { directory.AddError("Invalid Adobe JPEG data header."); return; } directory.SetInt(AdobeJpegDirectory.TagDctEncodeVersion, reader.GetUInt16()); directory.SetInt(AdobeJpegDirectory.TagApp14Flags0, reader.GetUInt16()); directory.SetInt(AdobeJpegDirectory.TagApp14Flags1, reader.GetUInt16()); directory.SetInt(AdobeJpegDirectory.TagColorTransform, reader.GetInt8()); } catch (IOException ex) { directory.AddError("IO exception processing data: " + ex.Message); } }
public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType) { if (metadata.ContainsDirectory <JpegDirectory>()) { // If this directory is already present, discontinue this operation. // We only store metadata for the *first* matching SOFn segment. return; } JpegDirectory directory = metadata.GetOrCreateDirectory <JpegDirectory>(); // 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); } }
/// <summary>Version specifically for dealing with XMP found in JPEG segments.</summary> /// <remarks> /// Version specifically for dealing with XMP found in JPEG segments. This form of XMP has a peculiar preamble, which /// must be removed before parsing the XML. /// </remarks> /// <param name="segmentBytes">The byte array from which the metadata should be extracted.</param> /// <param name="metadata"> /// The /// <see cref="Com.Drew.Metadata.Metadata"/> /// object into which extracted values should be merged. /// </param> /// <param name="segmentType"> /// The /// <see cref="Com.Drew.Imaging.Jpeg.JpegSegmentType"/> /// being read. /// </param> public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType) { XmpDirectory directory = metadata.GetOrCreateDirectory <XmpDirectory>(); // XMP in a JPEG file has a 29 byte preamble which is not valid XML. int preambleLength = 29; // check for the header length if (segmentBytes.Length <= preambleLength + 1) { directory.AddError(Sharpen.Extensions.StringFormat("Xmp data segment must contain at least %d bytes", preambleLength + 1)); return; } ByteArrayReader reader = new ByteArrayReader(segmentBytes); string preamble = Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, preambleLength); if (!"http://ns.adobe.com/xap/1.0/\x0".Equals(preamble)) { directory.AddError("XMP data segment doesn't begin with 'http://ns.adobe.com/xap/1.0/'"); return; } sbyte[] xmlBytes = new sbyte[segmentBytes.Length - preambleLength]; System.Array.Copy(segmentBytes, 29, xmlBytes, 0, xmlBytes.Length); Extract(xmlBytes, metadata); }
public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata) { PhotoshopDirectory directory = metadata.GetOrCreateDirectory <PhotoshopDirectory>(); int pos; try { pos = reader.GetString(0, 13).Equals("Photoshop 3.0") ? 14 : 0; } catch (IOException) { directory.AddError("Unable to read header"); return; } long length; try { length = reader.GetLength(); } catch (IOException e) { directory.AddError("Unable to read Photoshop data: " + e.Message); return; } while (pos < length) { try { // 4 bytes for the signature. Should always be "8BIM". //String signature = new String(data, pos, 4); pos += 4; // 2 bytes for the resource identifier (tag type). int tagType = reader.GetUInt16(pos); // segment type pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). int descriptionLength = reader.GetUInt16(pos); pos += 2; // Some basic bounds checking if (descriptionLength < 0 || descriptionLength + pos > length) { return; } //String description = new String(data, pos, descriptionLength); pos += descriptionLength; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { pos++; } // 4 bytes for the size of the resource data that follows. int byteCount = reader.GetInt32(pos); pos += 4; // The resource data. sbyte[] tagBytes = reader.GetBytes(pos, byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { pos++; } directory.SetByteArray(tagType, tagBytes); // TODO allow rebasing the reader with a new zero-point, rather than copying data here if (tagType == PhotoshopDirectory.TagIptc) { new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.Length); } 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 (IOException ex) { directory.AddError(ex.Message); return; } } }
/// <summary> /// Performs the IPTC data extraction, adding found values to the specified instance of /// <see cref="Com.Drew.Metadata.Metadata"/> /// . /// </summary> public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata, long length) { IptcDirectory directory = metadata.GetOrCreateDirectory <IptcDirectory>(); int offset = 0; // for each tag while (offset < length) { // identifies start of a tag short startByte; try { startByte = reader.GetUInt8(); offset++; } catch (IOException) { directory.AddError("Unable to read starting byte of IPTC tag"); return; } if (startByte != unchecked ((int)(0x1c))) { directory.AddError("Invalid start to IPTC tag"); return; } // we need at least five bytes left to read a tag if (offset + 5 >= length) { directory.AddError("Too few bytes remain for a valid IPTC tag"); return; } int directoryType; int tagType; int tagByteCount; try { directoryType = reader.GetUInt8(); tagType = reader.GetUInt8(); tagByteCount = reader.GetUInt16(); offset += 4; } catch (IOException) { directory.AddError("IPTC data segment ended mid-way through tag descriptor"); return; } if (offset + tagByteCount > length) { directory.AddError("Data for tag extends beyond end of IPTC segment"); return; } try { ProcessTag(reader, directory, directoryType, tagType, tagByteCount); } catch (IOException) { directory.AddError("Error processing IPTC tag"); return; } offset += tagByteCount; } }
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"); } }
public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata) { GifHeaderDirectory directory = metadata.GetOrCreateDirectory <GifHeaderDirectory>(); // FILE HEADER // // 3 - signature: "GIF" // 3 - version: either "87a" or "89a" // // LOGICAL SCREEN DESCRIPTOR // // 2 - pixel width // 2 - pixel height // 1 - screen and color map information flags (0 is LSB) // 0-2 Size of the global color table // 3 Color table sort flag (89a only) // 4-6 Color resolution // 7 Global color table flag // 1 - background color index // 1 - pixel aspect ratio reader.SetMotorolaByteOrder(false); try { string signature = reader.GetString(3); if (!signature.Equals("GIF")) { directory.AddError("Invalid GIF file signature"); return; } string version = reader.GetString(3); if (!version.Equals(Gif87aVersionIdentifier) && !version.Equals(Gif89aVersionIdentifier)) { directory.AddError("Unexpected GIF version"); return; } directory.SetString(GifHeaderDirectory.TagGifFormatVersion, version); directory.SetInt(GifHeaderDirectory.TagImageWidth, reader.GetUInt16()); directory.SetInt(GifHeaderDirectory.TagImageHeight, reader.GetUInt16()); short flags = reader.GetUInt8(); // First three bits = (BPP - 1) int colorTableSize = 1 << ((flags & 7) + 1); directory.SetInt(GifHeaderDirectory.TagColorTableSize, colorTableSize); if (version.Equals(Gif89aVersionIdentifier)) { bool isColorTableSorted = (flags & 8) != 0; directory.SetBoolean(GifHeaderDirectory.TagIsColorTableSorted, isColorTableSorted); } int bitsPerPixel = ((flags & unchecked ((int)(0x70))) >> 4) + 1; directory.SetInt(GifHeaderDirectory.TagBitsPerPixel, bitsPerPixel); bool hasGlobalColorTable = (flags & unchecked ((int)(0xf))) != 0; directory.SetBoolean(GifHeaderDirectory.TagHasGlobalColorTable, hasGlobalColorTable); directory.SetInt(GifHeaderDirectory.TagTransparentColorIndex, reader.GetUInt8()); int aspectRatioByte = reader.GetUInt8(); if (aspectRatioByte != 0) { float pixelAspectRatio = (float)((aspectRatioByte + 15d) / 64d); directory.SetFloat(GifHeaderDirectory.TagPixelAspectRatio, pixelAspectRatio); } } catch (IOException) { directory.AddError("Unable to read BMP header"); } }
protected internal DirectoryTiffHandler(Com.Drew.Metadata.Metadata metadata, Type initialDirectory) { _metadata = metadata; _currentDirectory = _metadata.GetOrCreateDirectory(initialDirectory); }
private static void ProcessMakernote(int makernoteOffset, ICollection <int> processedIfdOffsets, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader) { // Determine the camera model and makernote format Com.Drew.Metadata.Directory ifd0Directory = metadata.GetDirectory <ExifIFD0Directory>(); if (ifd0Directory == null) { return; } string cameraMake = ifd0Directory.GetString(ExifIFD0Directory.TagMake); string firstThreeChars = reader.GetString(makernoteOffset, 3); string firstFourChars = reader.GetString(makernoteOffset, 4); string firstFiveChars = reader.GetString(makernoteOffset, 5); string firstSixChars = reader.GetString(makernoteOffset, 6); string firstSevenChars = reader.GetString(makernoteOffset, 7); string firstEightChars = reader.GetString(makernoteOffset, 8); string firstTwelveChars = reader.GetString(makernoteOffset, 12); bool byteOrderBefore = reader.IsMotorolaByteOrder(); if ("OLYMP".Equals(firstFiveChars) || "EPSON".Equals(firstFiveChars) || "AGFA".Equals(firstFourChars)) { // Olympus Makernote // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ ProcessIFD(metadata.GetOrCreateDirectory <OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader); } else { if (cameraMake != null && Sharpen.Extensions.Trim(cameraMake).ToUpper().StartsWith("NIKON")) { if ("Nikon".Equals(firstFiveChars)) { switch (reader.GetUInt8(makernoteOffset + 6)) { case 1: { /* There are two scenarios here: * Type 1: ** * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ * Type 3: ** * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 */ ProcessIFD(metadata.GetOrCreateDirectory <NikonType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader); break; } case 2: { ProcessIFD(metadata.GetOrCreateDirectory <NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 18, makernoteOffset + 10, metadata, reader); break; } default: { ifd0Directory.AddError("Unsupported Nikon makernote data ignored."); break; } } } else { // The IFD begins with the first Makernote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models. ProcessIFD(metadata.GetOrCreateDirectory <NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader); } } else { if ("SONY CAM".Equals(firstEightChars) || "SONY DSC".Equals(firstEightChars)) { ProcessIFD(metadata.GetOrCreateDirectory <SonyType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader); } else { if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".Equals(firstTwelveChars)) { // force MM for this directory reader.SetMotorolaByteOrder(true); // skip 12 byte header + 2 for "MM" + 6 ProcessIFD(metadata.GetOrCreateDirectory <SonyType6MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 20, tiffHeaderOffset, metadata, reader); } else { if ("SIGMA\u0000\u0000\u0000".Equals(firstEightChars) || "FOVEON\u0000\u0000".Equals(firstEightChars)) { ProcessIFD(metadata.GetOrCreateDirectory <SigmaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 10, tiffHeaderOffset, metadata, reader); } else { if ("KDK".Equals(firstThreeChars)) { reader.SetMotorolaByteOrder(firstSevenChars.Equals("KDK INFO")); ProcessKodakMakernote(metadata.GetOrCreateDirectory <KodakMakernoteDirectory>(), makernoteOffset, reader); } else { if (Sharpen.Runtime.EqualsIgnoreCase("Canon", cameraMake)) { ProcessIFD(metadata.GetOrCreateDirectory <CanonMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader); } else { if (cameraMake != null && cameraMake.ToUpper().StartsWith("CASIO")) { if ("QVC\u0000\u0000\u0000".Equals(firstSixChars)) { ProcessIFD(metadata.GetOrCreateDirectory <CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, tiffHeaderOffset, metadata, reader); } else { ProcessIFD(metadata.GetOrCreateDirectory <CasioType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader); } } else { if ("FUJIFILM".Equals(firstEightChars) || Sharpen.Runtime.EqualsIgnoreCase("Fujifilm", cameraMake)) { // Note that this also applies to certain Leica cameras, such as the Digilux-4.3 reader.SetMotorolaByteOrder(false); // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote // IFD, though the offset is relative to the start of the makernote, not the TIFF // header (like everywhere else) int ifdStart = makernoteOffset + reader.GetInt32(makernoteOffset + 8); ProcessIFD(metadata.GetOrCreateDirectory <FujifilmMakernoteDirectory>(), processedIfdOffsets, ifdStart, makernoteOffset, metadata, reader); } else { if (cameraMake != null && cameraMake.ToUpper().StartsWith("MINOLTA")) { // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote // area that commences immediately. ProcessIFD(metadata.GetOrCreateDirectory <OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader); } else { if ("KYOCERA".Equals(firstSevenChars)) { // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html ProcessIFD(metadata.GetOrCreateDirectory <KyoceraMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 22, tiffHeaderOffset, metadata, reader); } else { if ("LEICA".Equals(firstFiveChars)) { reader.SetMotorolaByteOrder(false); if ("Leica Camera AG".Equals(cameraMake)) { ProcessIFD(metadata.GetOrCreateDirectory <LeicaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader); } else { if ("LEICA".Equals(cameraMake)) { // Some Leica cameras use Panasonic makernote tags ProcessIFD(metadata.GetOrCreateDirectory <PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader); } } } else { if ("Panasonic\u0000\u0000\u0000".Equals(reader.GetString(makernoteOffset, 12))) { // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html ProcessIFD(metadata.GetOrCreateDirectory <PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader); } else { if ("AOC\u0000".Equals(firstFourChars)) { // NON-Standard TIFF IFD Data using Casio Type 2 Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - Pentax ist D ProcessIFD(metadata.GetOrCreateDirectory <CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, makernoteOffset, metadata, reader); } else { if (cameraMake != null && (cameraMake.ToUpper().StartsWith("PENTAX") || cameraMake.ToUpper().StartsWith("ASAHI"))) { // NON-Standard TIFF IFD Data using Pentax Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - PENTAX Optio 330 // - PENTAX Optio 430 ProcessIFD(metadata.GetOrCreateDirectory <PentaxMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, makernoteOffset, metadata, reader); } else { // } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) { // // This Konica data is not understood. Header identified in accordance with information at this site: // // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html // // TODO add support for minolta/konica cameras // exifDirectory.addError("Unsupported Konica/Minolta data ignored."); if ("SANYO\x0\x1\x0".Equals(firstEightChars)) { ProcessIFD(metadata.GetOrCreateDirectory <SanyoMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, makernoteOffset, metadata, reader); } } } } } } } } } } } } } } } } // The makernote is not comprehended by this library. // If you are reading this and believe a particular camera's image should be processed, get in touch. reader.SetMotorolaByteOrder(byteOrderBefore); }
private static void ProcessIFD(Com.Drew.Metadata.Directory directory, ICollection <int> processedIfdOffsets, int ifdOffset, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader) { // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist if (processedIfdOffsets.Contains(Sharpen.Extensions.ValueOf(ifdOffset))) { return; } // remember that we've visited this directory so that we don't visit it again later processedIfdOffsets.Add(ifdOffset); if (ifdOffset >= reader.GetLength() || ifdOffset < 0) { directory.AddError("Ignored IFD marked to start outside data segment"); return; } // First two bytes in the IFD are the number of tags in this directory int dirTagCount = reader.GetUInt16(ifdOffset); int dirLength = (2 + (12 * dirTagCount) + 4); if (dirLength + ifdOffset > reader.GetLength()) { directory.AddError("Illegally sized IFD"); return; } // Handle each tag in this directory for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) { int tagOffset = CalculateTagOffset(ifdOffset, tagNumber); // 2 bytes for the tag type int tagType = reader.GetUInt16(tagOffset); // 2 bytes for the format code int formatCode = reader.GetUInt16(tagOffset + 2); if (formatCode < 1 || formatCode > MaxFormatCode) { // This error suggests that we are processing at an incorrect index and will generate // rubbish until we go out of bounds (which may be a while). Exit now. directory.AddError("Invalid TIFF tag format code: " + formatCode); return; } // 4 bytes dictate the number of components in this tag's data int componentCount = reader.GetInt32(tagOffset + 4); if (componentCount < 0) { directory.AddError("Negative TIFF tag component count"); continue; } // each component may have more than one byte... calculate the total number of bytes int byteCount = componentCount * BytesPerFormat[formatCode]; int tagValueOffset; if (byteCount > 4) { // If it's bigger than 4 bytes, the dir entry contains an offset. // dirEntryOffset must be passed, as some makernote implementations (e.g. Fujifilm) incorrectly use an // offset relative to the start of the makernote itself, not the TIFF segment. int offsetVal = reader.GetInt32(tagOffset + 8); if (offsetVal + byteCount > reader.GetLength()) { // Bogus pointer offset and / or byteCount value directory.AddError("Illegal TIFF tag pointer offset"); continue; } tagValueOffset = tiffHeaderOffset + offsetVal; } else { // 4 bytes or less and value is in the dir entry itself tagValueOffset = tagOffset + 8; } if (tagValueOffset < 0 || tagValueOffset > reader.GetLength()) { directory.AddError("Illegal TIFF tag pointer offset"); continue; } // Check that this tag isn't going to allocate outside the bounds of the data array. // This addresses an uncommon OutOfMemoryError. if (byteCount < 0 || tagValueOffset + byteCount > reader.GetLength()) { directory.AddError("Illegal number of bytes for TIFF tag data: " + byteCount); continue; } // // Special handling for certain known tags that point to or contain other chunks of data to be processed // if (tagType == ExifIFD0Directory.TagExifSubIfdOffset && directory is ExifIFD0Directory) { if (byteCount != 4) { directory.AddError("Exif SubIFD Offset tag should have a component count of four (bytes) for the offset."); } else { int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset); ProcessIFD(metadata.GetOrCreateDirectory <ExifSubIFDDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader); } } else { if (tagType == ExifSubIFDDirectory.TagInteropOffset && directory is ExifSubIFDDirectory) { if (byteCount != 4) { directory.AddError("Exif Interop Offset tag should have a component count of four (bytes) for the offset."); } else { int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset); ProcessIFD(metadata.GetOrCreateDirectory <ExifInteropDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader); } } else { if (tagType == ExifIFD0Directory.TagGpsInfoOffset && directory is ExifIFD0Directory) { if (byteCount != 4) { directory.AddError("Exif GPS Info Offset tag should have a component count of four (bytes) for the offset."); } else { int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset); ProcessIFD(metadata.GetOrCreateDirectory <GpsDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader); } } else { if (tagType == ExifSubIFDDirectory.TagMakernote && directory is ExifSubIFDDirectory) { // The makernote tag contains the encoded makernote data directly. // Pass the offset to this tag's value. Manufacturer/Model-specific logic will be used to // determine the correct offset for further processing. ProcessMakernote(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, metadata, reader); } else { ProcessTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader); } } } } } // at the end of each IFD is an optional link to the next IFD int finalTagOffset = CalculateTagOffset(ifdOffset, dirTagCount); int nextDirectoryOffset = reader.GetInt32(finalTagOffset); if (nextDirectoryOffset != 0) { nextDirectoryOffset += tiffHeaderOffset; if (nextDirectoryOffset >= reader.GetLength()) { // Last 4 bytes of IFD reference another IFD with an address that is out of bounds // Note this could have been caused by jhead 1.3 cropping too much return; } else { if (nextDirectoryOffset < ifdOffset) { // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory return; } } // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case ExifThumbnailDirectory nextDirectory = metadata.GetOrCreateDirectory <ExifThumbnailDirectory>(); ProcessIFD(nextDirectory, processedIfdOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader); } }
public static Com.Drew.Metadata.Metadata ReadMetadata(InputStream inputStream) { // TODO keep a single static hash of these ICollection <PngChunkType> desiredChunkTypes = new HashSet <PngChunkType>(); desiredChunkTypes.Add(PngChunkType.Ihdr); desiredChunkTypes.Add(PngChunkType.Plte); desiredChunkTypes.Add(PngChunkType.tRNS); desiredChunkTypes.Add(PngChunkType.cHRM); desiredChunkTypes.Add(PngChunkType.sRGB); desiredChunkTypes.Add(PngChunkType.gAMA); desiredChunkTypes.Add(PngChunkType.iCCP); desiredChunkTypes.Add(PngChunkType.bKGD); desiredChunkTypes.Add(PngChunkType.tEXt); desiredChunkTypes.Add(PngChunkType.iTXt); desiredChunkTypes.Add(PngChunkType.tIME); Iterable <PngChunk> chunks = new PngChunkReader().Extract(new Com.Drew.Lang.StreamReader(inputStream), desiredChunkTypes); Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata(); IList <KeyValuePair> textPairs = new AList <KeyValuePair>(); foreach (PngChunk chunk in chunks) { PngChunkType chunkType = chunk.GetChunkType(); sbyte[] bytes = chunk.GetBytes(); if (chunkType.Equals(PngChunkType.Ihdr)) { PngHeader header = new PngHeader(bytes); PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); 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()); } else { if (chunkType.Equals(PngChunkType.Plte)) { PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3); } else { if (chunkType.Equals(PngChunkType.tRNS)) { PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1); } else { if (chunkType.Equals(PngChunkType.sRGB)) { int srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8(); PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent); } else { if (chunkType.Equals(PngChunkType.cHRM)) { PngChromaticities chromaticities = new PngChromaticities(bytes); PngChromaticitiesDirectory directory = metadata.GetOrCreateDirectory <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()); } else { if (chunkType.Equals(PngChunkType.gAMA)) { int gammaInt = new SequentialByteArrayReader(bytes).GetInt32(); PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0); } else { if (chunkType.Equals(PngChunkType.iCCP)) { SequentialReader reader = new SequentialByteArrayReader(bytes); string profileName = reader.GetNullTerminatedString(79); PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetString(PngDirectory.TagProfileName, 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(); } } else { if (chunkType.Equals(PngChunkType.bKGD)) { PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes); } 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); textPairs.Add(new KeyValuePair(keyword, value)); } 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 { metadata.GetOrCreateDirectory <PngDirectory>().AddError("Invalid compression method value"); } } else { metadata.GetOrCreateDirectory <PngDirectory>().AddError("Invalid compression flag value"); } } 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 { textPairs.Add(new KeyValuePair(keyword, text)); } } } 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 = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime()); } } } } } } } } } } } } if (textPairs.Count != 0) { PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>(); directory.SetObject(PngDirectory.TagTextualData, textPairs); } return(metadata); }
protected internal virtual void PushDirectory <T>() where T : Com.Drew.Metadata.Directory { System.Diagnostics.Debug.Assert((typeof(T) != _currentDirectory.GetType())); _directoryStack.Push(_currentDirectory); _currentDirectory = _metadata.GetOrCreateDirectory <T>(); }
public static Com.Drew.Metadata.Metadata ReadMetadata(InputStream inputStream) { // TODO keep a single static hash of these ICollection<PngChunkType> desiredChunkTypes = new HashSet<PngChunkType>(); desiredChunkTypes.Add(PngChunkType.Ihdr); desiredChunkTypes.Add(PngChunkType.Plte); desiredChunkTypes.Add(PngChunkType.tRNS); desiredChunkTypes.Add(PngChunkType.cHRM); desiredChunkTypes.Add(PngChunkType.sRGB); desiredChunkTypes.Add(PngChunkType.gAMA); desiredChunkTypes.Add(PngChunkType.iCCP); desiredChunkTypes.Add(PngChunkType.bKGD); desiredChunkTypes.Add(PngChunkType.tEXt); desiredChunkTypes.Add(PngChunkType.iTXt); desiredChunkTypes.Add(PngChunkType.tIME); Iterable<PngChunk> chunks = new PngChunkReader().Extract(new Com.Drew.Lang.StreamReader(inputStream), desiredChunkTypes); Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata(); IList<KeyValuePair> textPairs = new AList<KeyValuePair>(); foreach (PngChunk chunk in chunks) { PngChunkType chunkType = chunk.GetChunkType(); sbyte[] bytes = chunk.GetBytes(); if (chunkType.Equals(PngChunkType.Ihdr)) { PngHeader header = new PngHeader(bytes); PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); 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()); } else { if (chunkType.Equals(PngChunkType.Plte)) { PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3); } else { if (chunkType.Equals(PngChunkType.tRNS)) { PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1); } else { if (chunkType.Equals(PngChunkType.sRGB)) { int srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8(); PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent); } else { if (chunkType.Equals(PngChunkType.cHRM)) { PngChromaticities chromaticities = new PngChromaticities(bytes); PngChromaticitiesDirectory directory = metadata.GetOrCreateDirectory<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()); } else { if (chunkType.Equals(PngChunkType.gAMA)) { int gammaInt = new SequentialByteArrayReader(bytes).GetInt32(); PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0); } else { if (chunkType.Equals(PngChunkType.iCCP)) { SequentialReader reader = new SequentialByteArrayReader(bytes); string profileName = reader.GetNullTerminatedString(79); PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetString(PngDirectory.TagProfileName, 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(); } } else { if (chunkType.Equals(PngChunkType.bKGD)) { PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes); } 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); textPairs.Add(new KeyValuePair(keyword, value)); } 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 { metadata.GetOrCreateDirectory<PngDirectory>().AddError("Invalid compression method value"); } } else { metadata.GetOrCreateDirectory<PngDirectory>().AddError("Invalid compression flag value"); } } 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 { textPairs.Add(new KeyValuePair(keyword, text)); } } } 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 = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime()); } } } } } } } } } } } } if (textPairs.Count != 0) { PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>(); directory.SetObject(PngDirectory.TagTextualData, textPairs); } return metadata; }