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); }
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)); }