ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { // ICC data can be spread across multiple JPEG segments. // Skip any segments that do not contain the required preamble var iccSegments = segments.Where(segment => segment.Length > SegmentHeaderLength && IsSubarrayEqualTo(segment, 0, _jpegSegmentPreambleBytes)).ToList(); if (iccSegments.Count == 0) { return(new Directory[0]); } byte[] buffer; if (iccSegments.Count == 1) { buffer = new byte[iccSegments[0].Length - SegmentHeaderLength]; Array.Copy(iccSegments[0], SegmentHeaderLength, buffer, 0, iccSegments[0].Length - SegmentHeaderLength); } else { // Concatenate all buffers var totalLength = iccSegments.Sum(s => s.Length - SegmentHeaderLength); buffer = new byte[totalLength]; for (int i = 0, pos = 0; i < iccSegments.Count; i++) { var segment = iccSegments[i]; Array.Copy(segment, SegmentHeaderLength, buffer, pos, segment.Length - SegmentHeaderLength); pos += segment.Length - SegmentHeaderLength; } } return(new[] { Extract(new ByteArrayReader(buffer)) }); }
/// <exception cref="System.IO.IOException"/> /// <exception cref="Com.Drew.Imaging.Jpeg.JpegProcessingException"/> public static void Main(string[] args) { if (args.Length < 1) { PrintUsage(); System.Environment.Exit(1); } string filePath = args[0]; if (!new FilePath(filePath).Exists()) { System.Console.Error.Println("File does not exist"); PrintUsage(); System.Environment.Exit(1); } ICollection <JpegSegmentType> segmentTypes = new HashSet <JpegSegmentType>(); for (int i = 1; i < args.Length; i++) { JpegSegmentType segmentType = JpegSegmentType.ValueOf(args[i].ToUpper()); if (!segmentType.canContainMetadata) { System.Console.Error.Printf("WARNING: Segment type %s cannot contain metadata so it may not be necessary to extract it%n", segmentType); } segmentTypes.Add(segmentType); } if (segmentTypes.Count == 0) { // If none specified, use all that could reasonably contain metadata Sharpen.Collections.AddAll(segmentTypes, JpegSegmentType.canContainMetadataTypes); } JpegSegmentData segmentData = JpegSegmentReader.ReadSegments(new FilePath(filePath), segmentTypes.AsIterable()); SaveSegmentFiles(filePath, segmentData); }
/// <summary> /// Split data array to segments of length at most <paramref name="maxSegmentSize"/> bytes /// </summary> /// <param name="data">Data to split</param> /// <param name="type">Type of each JPEG segment</param> /// <param name="header"> /// Header of each JPEG segment. All characters have to be ASCII. /// </param> /// <param name="maxSegmentSize"> /// Maximal size of the JPEG segment (excluding the 2 size bytes) /// </param> /// <returns>JPEG segments enumerator</returns> public static IEnumerable <JpegSegment> SplitSegmentData( byte[] data, JpegSegmentType type, string header, long maxSegmentSize = MaxSegmentSize) { if (header.Length >= maxSegmentSize) { throw new ArgumentException( "Header must fit in the JPEG segment with some data."); } long dataOffset = 0; while (dataOffset < data.Length) { long bufferSize = Math.Min( data.Length - dataOffset, maxSegmentSize - header.Length); var buffer = new byte[bufferSize + header.Length]; // copy header for (int i = 0; i < header.Length; ++i) { buffer[i] = (byte)header[i]; } // copy part of the data Array.Copy(data, dataOffset, buffer, header.Length, bufferSize); dataOffset += bufferSize; yield return(new JpegSegment(type, buffer, 0)); } }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { var preambleLength = JpegSegmentPreamble.Length; return segments .Where(segment => segment.Length >= preambleLength + 1 && JpegSegmentPreamble == Encoding.UTF8.GetString(segment, 0, preambleLength)) .SelectMany(segment => Extract(new SequentialByteArrayReader(segment, preambleLength + 1), segment.Length - preambleLength - 1)) .ToList(); }
public JpegSegment(JpegSegmentType type, int length, int padding, long offset, string preamble) { Type = type; Length = length; Padding = padding; Offset = offset; Preamble = preamble; }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { Debug.Assert(segmentType == JpegSegmentType.App1); return segments .Where(segment => segment.Length >= JpegSegmentPreamble.Length && Encoding.UTF8.GetString(segment, 0, JpegSegmentPreamble.Length) == JpegSegmentPreamble) .SelectMany(segment => Extract(new ByteArrayReader(segment), JpegSegmentPreamble.Length)) .ToList(); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { var preambleLength = JpegSegmentPreamble.Length; return(segments .Where(segment => segment.Length >= preambleLength + 1 && JpegSegmentPreamble == Encoding.UTF8.GetString(segment, 0, preambleLength)) .SelectMany(segment => Extract(new SequentialByteArrayReader(segment, preambleLength + 1), segment.Length - preambleLength - 1)) .ToList()); }
/// <summary>Removes a specified instance of a segment's data from the collection.</summary> /// <remarks> /// Removes a specified instance of a segment's data from the collection. Use this method when more than one /// occurrence of segment data exists for a given type exists. /// </remarks> /// <param name="segmentType">identifies the required segment</param> /// <param name="occurrence">the zero-based index of the segment occurrence to remove.</param> public void RemoveSegmentOccurrence(JpegSegmentType segmentType, int occurrence) { IList <byte[]> segmentList; if (_segmentDataMap.TryGetValue(segmentType, out segmentList)) { segmentList.RemoveAt(occurrence); } }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { Debug.Assert(segmentType == JpegSegmentType.App1); return(segments .Where(segment => segment.Length >= JpegSegmentPreamble.Length && Encoding.UTF8.GetString(segment, 0, JpegSegmentPreamble.Length) == JpegSegmentPreamble) .SelectMany(segment => Extract(new ByteArrayReader(segment), JpegSegmentPreamble.Length)) .ToList()); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { return(segments .Where(segment => segment.Length == 12 && Preamble.Equals(Encoding.ASCII.GetString(segment, 0, Preamble.Length), StringComparison.OrdinalIgnoreCase)) .Select(bytes => Extract(new SequentialByteArrayReader(bytes))) #if NET35 .Cast <Directory>() #endif .ToList()); }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { return segments .Where(segment => segment.Length == 12 && Preamble.Equals(Encoding.ASCII.GetString(segment, 0, Preamble.Length), StringComparison.OrdinalIgnoreCase)) .Select(bytes => Extract(new SequentialByteArrayReader(bytes))) #if NET35 .Cast<Directory>() #endif .ToList(); }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { // Ensure data starts with the IPTC marker byte return segments .Where(segment => segment.Length != 0 && segment[0] == IptcMarkerByte) .Select(segment => Extract(new SequentialByteArrayReader(segment), segment.Length)) #if NET35 .Cast<Directory>() #endif .ToList(); }
private IList <byte[]> GetOrCreateSegmentList(JpegSegmentType segmentType) { IList <byte[]> segmentList; if (!_segmentDataMap.TryGetValue(segmentType, out segmentList)) { segmentList = new List <byte[]>(); _segmentDataMap[segmentType] = segmentList; } return(segmentList); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { // Ensure data starts with the IPTC marker byte return(segments .Where(segment => segment.Length != 0 && segment[0] == IptcMarkerByte) .Select(segment => Extract(new SequentialByteArrayReader(segment), segment.Length)) #if NET35 .Cast <Directory>() #endif .ToList()); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { // Skip segments not starting with the required header return(segments .Where(segment => segment.Length >= Preamble.Length && Preamble == Encoding.ASCII.GetString(segment, 0, Preamble.Length)) .Select(segment => Extract(new SequentialByteArrayReader(segment, Preamble.Length))) #if NET35 .Cast <Directory>() #endif .ToList()); }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { // Skip segments not starting with the required header return segments .Where(segment => segment.Length >= Preamble.Length && Preamble == Encoding.ASCII.GetString(segment, 0, Preamble.Length)) .Select(segment => Extract(new SequentialByteArrayReader(segment, Preamble.Length))) #if NET35 .Cast<Directory>() #endif .ToList(); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { Debug.Assert(segmentType == JpegSegmentType.Com); // TODO store bytes in the directory to allow different encodings when decoding // The entire contents of the segment are the comment return(segments.Select(segment => new JpegCommentDirectory(Encoding.UTF8.GetString(segment))) #if NET35 .Cast <Directory>() #endif .ToList()); }
/// <summary>Gets whether this JPEG segment type might contain metadata.</summary> /// <remarks>Used to exclude large image-data-only segment from certain types of processing.</remarks> public static bool CanContainMetadata(this JpegSegmentType type) { switch (type) { case JpegSegmentType.Soi: case JpegSegmentType.Dqt: case JpegSegmentType.Dht: return(false); default: return(true); } }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { Debug.Assert(segmentType == JpegSegmentType.Com); // TODO store bytes in the directory to allow different encodings when decoding // The entire contents of the segment are the comment return segments.Select(segment => new JpegCommentDirectory(Encoding.UTF8.GetString(segment))) #if NET35 .Cast<Directory>() #endif .ToList(); }
/// <summary> /// Check segment type and header. /// </summary> /// <param name="segment">JPEG segment</param> /// <param name="type">Expected type</param> /// <param name="header">Expected header</param> /// <returns>true iff the segment has given type and header</returns> public static bool MatchSegment(JpegSegment segment, JpegSegmentType type, string header) { if (segment.Type != type || segment.Bytes.Length < header.Length) { return(false); } for (int i = 0; i < header.Length; ++i) { if ((char)segment.Bytes[i] != header[i]) { return(false); } } return(true); }
ReadJpegSegments(IEnumerable <byte[]> segments, JpegSegmentType segmentType) { var directories = new List <Directory>(); foreach (var segmentBytes in segments) { // XMP in a JPEG file has an identifying preamble which is not valid XML var preambleLength = XmpJpegPreamble.Length; if (segmentBytes.Length >= preambleLength && XmpJpegPreamble.Equals(Encoding.UTF8.GetString(segmentBytes, 0, preambleLength), StringComparison.OrdinalIgnoreCase)) { var xmlBytes = new byte[segmentBytes.Length - preambleLength]; Array.Copy(segmentBytes, preambleLength, xmlBytes, 0, xmlBytes.Length); directories.Add(Extract(xmlBytes)); } } return(directories); }
ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) { var directories = new List<Directory>(); foreach (var segmentBytes in segments) { // XMP in a JPEG file has an identifying preamble which is not valid XML var preambleLength = XmpJpegPreamble.Length; if (segmentBytes.Length >= preambleLength && XmpJpegPreamble.Equals(Encoding.UTF8.GetString(segmentBytes, 0, preambleLength), StringComparison.OrdinalIgnoreCase)) { var xmlBytes = new byte[segmentBytes.Length - preambleLength]; Array.Copy(segmentBytes, preambleLength, xmlBytes, 0, xmlBytes.Length); directories.Add(Extract(xmlBytes)); } } return directories; }
/// <summary>Gets whether this JPEG segment type's marker is followed by a length indicator.</summary> public static bool ContainsPayload(this JpegSegmentType type) { // ReSharper disable once SwitchStatementMissingSomeCases switch (type) { case JpegSegmentType.Soi: case JpegSegmentType.Eoi: case JpegSegmentType.Rst0: case JpegSegmentType.Rst1: case JpegSegmentType.Rst2: case JpegSegmentType.Rst3: case JpegSegmentType.Rst4: case JpegSegmentType.Rst5: case JpegSegmentType.Rst6: case JpegSegmentType.Rst7: return(false); default: return(true); } }
/// <summary> /// Copy segment data from multiple segments without header to a single array. /// </summary> /// <param name="segments">All segments</param> /// <param name="type">Type of segments to copy</param> /// <param name="header">Header of the segments to copy (first bytes, ASCII characters only)</param> /// <returns></returns> public static byte[] JoinSegmentData( IEnumerable <JpegSegment> segments, JpegSegmentType type, string header) { // compute the exact size long size = 0; foreach (var segment in segments) { if (MatchSegment(segment, type, header)) { size += segment.Bytes.Length - header.Length; } } // copy segments var buffer = new byte[size]; long bufferOffset = 0; foreach (var segment in segments) { if (MatchSegment(segment, type, header)) { var bytesToCopyCount = segment.Bytes.Length - header.Length; Array.Copy( segment.Bytes, header.Length, buffer, bufferOffset, bytesToCopyCount); bufferOffset += bytesToCopyCount; } } return(buffer); }
/// <summary>Reads JPEG SOF values and returns them in a <see cref="JpegDirectory"/>.</summary> public JpegDirectory Extract(byte[] segmentBytes, JpegSegmentType segmentType) { var directory = new JpegDirectory(); // The value of TagCompressionType is determined by the segment type found directory.Set(JpegDirectory.TagCompressionType, (int)segmentType - (int)JpegSegmentType.Sof0); SequentialReader reader = new SequentialByteArrayReader(segmentBytes); 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 < (int)componentCount; i++) { int componentId = reader.GetByte(); int samplingFactorByte = reader.GetByte(); int 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); }
/// <summary>Reads JPEG SOF values and returns them in a <see cref="JpegDirectory"/>.</summary> public JpegDirectory Extract(byte[] segmentBytes, JpegSegmentType segmentType) { var directory = new JpegDirectory(); // The value of TagCompressionType is determined by the segment type found directory.Set(JpegDirectory.TagCompressionType, (int)segmentType - (int)JpegSegmentType.Sof0); SequentialReader reader = new SequentialByteArrayReader(segmentBytes); 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 < (int)componentCount; i++) { int componentId = reader.GetByte(); int samplingFactorByte = reader.GetByte(); int 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; }
public static IList <Directory> ProcessSegmentBytes(string filePath, JpegSegmentType type) { var segment = new JpegSegment(type, TestDataUtil.GetBytes(filePath), 0); return(new ExifReader().ReadJpegSegments(new[] { segment }).ToList()); }
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 bool CanProcess(sbyte[] segmentBytes, JpegSegmentType segmentType) { // The entire contents of the byte[] is the comment. There's nothing here to discriminate upon. return(true); }
/// <summary>Removes all segments from the collection having the specified type.</summary> /// <param name="segmentType">identifies the required segment</param> public void RemoveAllSegments(JpegSegmentType segmentType) { _segmentDataMap.Remove(segmentType); }
public virtual void ReadJpegSegments([NotNull] Iterable <sbyte[]> segments, [NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] JpegSegmentType segmentType) { foreach (sbyte[] segmentBytes in segments) { // Ensure data starts with the IPTC marker byte if (segmentBytes.Length != 0 && segmentBytes[0] == unchecked ((int)(0x1c))) { Extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.Length); } } }
public JpegSegment(JpegSegmentType type, byte[] bytes, long offset) { Type = type; Bytes = bytes; Offset = offset; }
private IList<byte[]> GetSegmentList(JpegSegmentType segmentType) { IList<byte[]> list; return _segmentDataMap.TryGetValue(segmentType, out list) ? list : null; }
public virtual bool CanProcess(sbyte[] segmentBytes, JpegSegmentType segmentType) { return(segmentBytes.Length > 12 && "Photoshop 3.0".Equals(Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, 13))); }
public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType) { Extract(new SequentialByteArrayReader(segmentBytes), metadata); }
/// <summary>Adds segment bytes to the collection.</summary> /// <param name="segmentType">the type of the segment being added</param> /// <param name="segmentBytes">the byte array holding data for the segment being added</param> public void AddSegment(JpegSegmentType segmentType, [NotNull] byte[] segmentBytes) { GetOrCreateSegmentList(segmentType).Add(segmentBytes); }
public byte[] GetSegment(JpegSegmentType segmentType, int occurrence = 0) { var segmentList = GetSegmentList(segmentType); return segmentList != null && segmentList.Count > occurrence ? segmentList[occurrence] : null; }
public IEnumerable<byte[]> GetSegments(JpegSegmentType segmentType) => GetSegmentList(segmentType) ?? Enumerable.Empty<byte[]>();
public static T ProcessSegmentBytes <T>(string filePath, JpegSegmentType type) where T : Directory { return(ProcessSegmentBytes(filePath, type).OfType <T>().First()); }
/// <summary>Determines whether data is present for a given segment type.</summary> /// <param name="segmentType">identifies the required segment</param> /// <returns>true if data exists, otherwise false</returns> public bool ContainsSegment(JpegSegmentType segmentType) { return _segmentDataMap.ContainsKey(segmentType); }
public virtual bool CanProcess(sbyte[] segmentBytes, JpegSegmentType segmentType) { return(segmentBytes.Length == 12 && Sharpen.Runtime.EqualsIgnoreCase("Adobe", Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, 5))); }
/// <summary>Removes a specified instance of a segment's data from the collection.</summary> /// <remarks> /// Removes a specified instance of a segment's data from the collection. Use this method when more than one /// occurrence of segment data exists for a given type exists. /// </remarks> /// <param name="segmentType">identifies the required segment</param> /// <param name="occurrence">the zero-based index of the segment occurrence to remove.</param> public void RemoveSegmentOccurrence(JpegSegmentType segmentType, int occurrence) { IList<byte[]> segmentList; if (_segmentDataMap.TryGetValue(segmentType, out segmentList)) { segmentList.RemoveAt(occurrence); } }
private IList<byte[]> GetOrCreateSegmentList(JpegSegmentType segmentType) { IList<byte[]> segmentList; if (!_segmentDataMap.TryGetValue(segmentType, out segmentList)) { segmentList = new List<byte[]>(); _segmentDataMap[segmentType] = segmentList; } return segmentList; }
/// <summary>Gets whether this JPEG segment is intended to hold application specific data.</summary> public static bool IsApplicationSpecific(this JpegSegmentType type) => type >= JpegSegmentType.App0 && type <= JpegSegmentType.AppF;
public IReadOnlyList<Directory> ReadJpegSegments(IEnumerable<byte[]> segments, JpegSegmentType segmentType) => segments.Select(segmentBytes => Extract(segmentBytes, segmentType)).ToList();
/// <summary>Returns the count of segment data byte arrays stored for a given segment type.</summary> /// <param name="segmentType">identifies the required segment</param> /// <returns>the segment count (zero if no segments exist).</returns> public int GetSegmentCount(JpegSegmentType segmentType) { return GetSegmentList(segmentType)?.Count ?? 0; }
public virtual void ReadJpegSegments([NotNull] Iterable <sbyte[]> segments, [NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] JpegSegmentType segmentType) { System.Diagnostics.Debug.Assert((segmentType == JpegSegmentType.App1)); foreach (sbyte[] segmentBytes in segments) { // Filter any segments containing unexpected preambles if (segmentBytes.Length < JpegSegmentPreamble.Length || !Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, JpegSegmentPreamble.Length).Equals(JpegSegmentPreamble)) { continue; } Extract(new ByteArrayReader(segmentBytes), metadata, JpegSegmentPreamble.Length); } }
/// <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="segments">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 ReadJpegSegments([NotNull] Iterable <sbyte[]> segments, [NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] JpegSegmentType segmentType) { foreach (sbyte[] segmentBytes in segments) { // XMP in a JPEG file has an identifying preamble which is not valid XML int preambleLength = XmpJpegPreamble.Length; if (segmentBytes.Length < preambleLength || !Sharpen.Runtime.EqualsIgnoreCase(XmpJpegPreamble, Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, preambleLength))) { continue; } sbyte[] xmlBytes = new sbyte[segmentBytes.Length - preambleLength]; System.Array.Copy(segmentBytes, preambleLength, xmlBytes, 0, xmlBytes.Length); Extract(xmlBytes, metadata); } }