private void WriteDirectory(BinaryWriter writer, Dictionary <ushort, MetadataEntry> tags, List <IFDEntry> entries, long ifdOffset) { writer.BaseStream.Position = ifdOffset; long nextIFDPointerOffset = ifdOffset + sizeof(ushort) + ((long)entries.Count * IFDEntry.SizeOf); writer.Write((ushort)entries.Count); foreach (IFDEntry entry in entries.OrderBy(e => e.Tag)) { entry.Write(writer); if (!TagDataTypeUtil.ValueFitsInOffsetField(entry.Type, entry.Count)) { long oldPosition = writer.BaseStream.Position; writer.BaseStream.Position = entry.Offset; writer.Write(tags[entry.Tag].GetDataReadOnly()); writer.BaseStream.Position = oldPosition; } } writer.BaseStream.Position = nextIFDPointerOffset; // There is only one IFD in this directory. writer.Write(0); }
public System.Drawing.Imaging.PropertyItem TryCreateGdipPropertyItem() { // Skip the Interoperability IFD because GDI+ does not support it. // https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-constant-image-property-tag-constants if (!TagDataTypeUtil.IsKnownToGDIPlus(Type) || Section == MetadataSection.Interop) { return(null); } System.Drawing.Imaging.PropertyItem propertyItem = PaintDotNet.SystemLayer.PdnGraphics.CreatePropertyItem(); propertyItem.Id = TagId; propertyItem.Type = (short)Type; propertyItem.Len = data.Length; propertyItem.Value = (byte[])data.Clone(); return(propertyItem); }
private string GetValueStringFromOffset() { string valueString; TagDataType type = entry.Type; uint count = entry.Count; uint offset = entry.Offset; int typeSizeInBytes = TagDataTypeUtil.GetSizeInBytes(type); if (typeSizeInBytes == 1) { byte[] bytes = new byte[count]; if (offsetIsBigEndian) { switch (count) { case 1: bytes[0] = (byte)((offset >> 24) & 0x000000ff); break; case 2: bytes[0] = (byte)((offset >> 24) & 0x000000ff); bytes[1] = (byte)((offset >> 16) & 0x000000ff); break; case 3: bytes[0] = (byte)((offset >> 24) & 0x000000ff); bytes[1] = (byte)((offset >> 16) & 0x000000ff); bytes[2] = (byte)((offset >> 8) & 0x000000ff); break; case 4: bytes[0] = (byte)((offset >> 24) & 0x000000ff); bytes[1] = (byte)((offset >> 16) & 0x000000ff); bytes[2] = (byte)((offset >> 8) & 0x000000ff); bytes[3] = (byte)(offset & 0x000000ff); break; } } else { switch (count) { case 1: bytes[0] = (byte)(offset & 0x000000ff); break; case 2: bytes[0] = (byte)(offset & 0x000000ff); bytes[1] = (byte)((offset >> 8) & 0x000000ff); break; case 3: bytes[0] = (byte)(offset & 0x000000ff); bytes[1] = (byte)((offset >> 8) & 0x000000ff); bytes[2] = (byte)((offset >> 16) & 0x000000ff); break; case 4: bytes[0] = (byte)(offset & 0x000000ff); bytes[1] = (byte)((offset >> 8) & 0x000000ff); bytes[2] = (byte)((offset >> 16) & 0x000000ff); bytes[3] = (byte)((offset >> 24) & 0x000000ff); break; } } if (type == TagDataType.Ascii) { valueString = Encoding.ASCII.GetString(bytes).TrimEnd('\0'); } else if (count == 1) { valueString = bytes[0].ToString(CultureInfo.InvariantCulture); } else { StringBuilder builder = new StringBuilder(); uint lastItemIndex = count - 1; for (int i = 0; i < count; i++) { builder.Append(bytes[i].ToString(CultureInfo.InvariantCulture)); if (i < lastItemIndex) { builder.Append(","); } } valueString = builder.ToString(); } } else if (typeSizeInBytes == 2) { ushort[] values = new ushort[count]; if (offsetIsBigEndian) { switch (count) { case 1: values[0] = (ushort)((offset >> 16) & 0x0000ffff); break; case 2: values[0] = (ushort)((offset >> 16) & 0x0000ffff); values[1] = (ushort)(offset & 0x0000ffff); break; } } else { switch (count) { case 1: values[0] = (ushort)(offset & 0x0000ffff); break; case 2: values[0] = (ushort)(offset & 0x0000ffff); values[1] = (ushort)((offset >> 16) & 0x0000ffff); break; } } if (count == 1) { switch (type) { case TagDataType.SShort: valueString = ((short)values[0]).ToString(CultureInfo.InvariantCulture); break; case TagDataType.Short: default: valueString = values[0].ToString(CultureInfo.InvariantCulture); break; } } else { switch (type) { case TagDataType.SShort: valueString = ((short)values[0]).ToString(CultureInfo.InvariantCulture) + "," + ((short)values[1]).ToString(CultureInfo.InvariantCulture); break; case TagDataType.Short: default: valueString = values[0].ToString(CultureInfo.InvariantCulture) + "," + values[1].ToString(CultureInfo.InvariantCulture); break; } } } else { valueString = offset.ToString(CultureInfo.InvariantCulture); } return(valueString); }
private static IFDEntryInfo CreateIFDList(Dictionary <ushort, MetadataEntry> tags, long startOffset) { List <IFDEntry> ifdEntries = new List <IFDEntry>(tags.Count); // Leave room for the tag count, tags and next IFD offset. long ifdDataOffset = startOffset + sizeof(ushort) + ((long)tags.Count * IFDEntry.SizeOf) + sizeof(uint); foreach (KeyValuePair <ushort, MetadataEntry> item in tags.OrderBy(i => i.Key)) { MetadataEntry entry = item.Value; uint count; switch (entry.Type) { case TagDataType.Byte: case TagDataType.Ascii: case TagDataType.SByte: case TagDataType.Undefined: count = (uint)entry.LengthInBytes; break; case TagDataType.Short: case TagDataType.SShort: count = (uint)entry.LengthInBytes / 2; break; case TagDataType.Long: case TagDataType.SLong: case TagDataType.Float: count = (uint)entry.LengthInBytes / 4; break; case TagDataType.Rational: case TagDataType.SRational: case TagDataType.Double: count = (uint)entry.LengthInBytes / 8; break; default: throw new InvalidOperationException("Unexpected tag type."); } if (TagDataTypeUtil.ValueFitsInOffsetField(entry.Type, count)) { uint packedOffset = 0; // Some applications may write EXIF fields with a count of zero. // See https://github.com/0xC0000054/pdn-webp/issues/6. if (count > 0) { byte[] data = entry.GetDataReadOnly(); // The data is always in little-endian byte order. switch (data.Length) { case 1: packedOffset |= data[0]; break; case 2: packedOffset |= data[0]; packedOffset |= (uint)data[1] << 8; break; case 3: packedOffset |= data[0]; packedOffset |= (uint)data[1] << 8; packedOffset |= (uint)data[2] << 16; break; case 4: packedOffset |= data[0]; packedOffset |= (uint)data[1] << 8; packedOffset |= (uint)data[2] << 16; packedOffset |= (uint)data[3] << 24; break; default: throw new InvalidOperationException("data.Length must be in the range of [1-4]."); } } ifdEntries.Add(new IFDEntry(entry.TagId, entry.Type, count, packedOffset)); } else { ifdEntries.Add(new IFDEntry(entry.TagId, entry.Type, count, (uint)ifdDataOffset)); ifdDataOffset += entry.LengthInBytes; // The IFD offsets must begin on a WORD boundary. if ((ifdDataOffset & 1) == 1) { ifdDataOffset++; } } } return(new IFDEntryInfo(ifdEntries, startOffset, ifdDataOffset)); }
private static ICollection <MetadataEntry> ConvertIFDEntriesToMetadataEntries(EndianBinaryReader reader, List <ParserIFDEntry> entries) { List <MetadataEntry> metadataEntries = new List <MetadataEntry>(entries.Count); bool swapNumberByteOrder = reader.Endianess == Endianess.Big; for (int i = 0; i < entries.Count; i++) { ParserIFDEntry entry = entries[i]; byte[] propertyData; if (entry.OffsetFieldContainsValue) { propertyData = entry.GetValueBytesFromOffset(); if (propertyData == null) { continue; } } else { long bytesToRead = entry.Count * TagDataTypeUtil.GetSizeInBytes(entry.Type); // Skip any tags that are empty or larger than 2 GB. if (bytesToRead == 0 || bytesToRead > int.MaxValue) { continue; } uint offset = entry.Offset; if ((offset + bytesToRead) > reader.Length) { continue; } reader.Position = offset; propertyData = reader.ReadBytes((int)bytesToRead); if (swapNumberByteOrder) { // Paint.NET converts all multi-byte numbers to little-endian. switch (entry.Type) { case TagDataType.Short: case TagDataType.SShort: propertyData = SwapShortArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Long: case TagDataType.SLong: case TagDataType.Float: propertyData = SwapLongArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Rational: case TagDataType.SRational: propertyData = SwapRationalArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Double: propertyData = SwapDoubleArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Byte: case TagDataType.Ascii: case TagDataType.Undefined: case TagDataType.SByte: default: break; } } } metadataEntries.Add(new MetadataEntry(entry.Section, entry.Tag, entry.Type, propertyData)); } return(metadataEntries); }