Example #1
0
        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);
        }
Example #2
0
        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 is 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);
        }
Example #3
0
            private string GetValueStringFromOffset()
            {
                string valueString;

                TagDataType type   = this.entry.Type;
                uint        count  = this.entry.Count;
                uint        offset = this.entry.Offset;

                if (count == 0)
                {
                    return(string.Empty);
                }

                int typeSizeInBytes = TagDataTypeUtil.GetSizeInBytes(type);

                if (typeSizeInBytes == 1)
                {
                    byte[] bytes = new byte[count];

                    if (this.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 (this.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);
            }
Example #4
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));
        }