Browse Source

Improve behavior and add comments

pull/3047/head
James Jackson-South 4 months ago
parent
commit
81d533b320
  1. 32
      src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs
  2. 2
      tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs

32
src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs

@ -46,7 +46,7 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// <summary> /// <summary>
/// Convert the content of this <see cref="XmpProfile"/> into an <see cref="XDocument"/>. /// Convert the content of this <see cref="XmpProfile"/> into an <see cref="XDocument"/>.
/// </summary> /// </summary>
/// <returns>The <see cref="XDocument"/></returns> /// <returns>The <see cref="XDocument"/> instance, or <see langword="null"/> if no XMP data is present.</returns>
public XDocument? ToXDocument() public XDocument? ToXDocument()
{ {
byte[]? data = this.Data; byte[]? data = this.Data;
@ -74,8 +74,14 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// <returns>The <see cref="T:Byte[]"/></returns> /// <returns>The <see cref="T:Byte[]"/></returns>
public byte[] ToByteArray() public byte[] ToByteArray()
{ {
Guard.NotNull(this.Data); byte[]? data = this.Data;
byte[] result = new byte[this.Data.Length];
if (data is null)
{
return [];
}
byte[] result = new byte[data.Length];
this.Data.AsSpan().CopyTo(result); this.Data.AsSpan().CopyTo(result);
return result; return result;
} }
@ -83,10 +89,15 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
/// <inheritdoc/> /// <inheritdoc/>
public XmpProfile DeepClone() public XmpProfile DeepClone()
{ {
Guard.NotNull(this.Data); byte[]? data = this.Data;
if (data is null)
{
// Preserve the semantics of an "empty" profile when cloning.
return new XmpProfile();
}
byte[] clone = new byte[this.Data.Length]; byte[] clone = new byte[data.Length];
this.Data.AsSpan().CopyTo(clone); data.AsSpan().CopyTo(clone);
return new XmpProfile(clone); return new XmpProfile(clone);
} }
@ -118,7 +129,14 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
} }
// Allocation-free fast path for the normal case. // Allocation-free fast path for the normal case.
// Check for UTF-8 BOM (0xEF,0xBB,0xBF)
bool hasBom = data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF; bool hasBom = data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF;
// XMP metadata is commonly stored in fixed-size container blocks (e.g. TIFF tag 700).
// Producers often pad unused space so the packet can be updated in-place without
// rewriting the file. In practice this padding is either NUL (0x00) from the container
// or 0x0F used by Adobe XMP writers. Both are invalid XML and must be trimmed.
bool hasTrailingPad = data[^1] is 0 or 0x0F; bool hasTrailingPad = data[^1] is 0 or 0x0F;
if (!hasBom && !hasTrailingPad) if (!hasBom && !hasTrailingPad)
@ -146,7 +164,7 @@ public sealed class XmpProfile : IDeepCloneable<XmpProfile>
int length = end - start; int length = end - start;
if (length <= 0) if (length <= 0)
{ {
return []; return null;
} }
byte[] normalized = new byte[length]; byte[] normalized = new byte[length];

2
tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs

@ -79,7 +79,7 @@ public class XmpProfileTests
} }
[Fact] [Fact]
public void XmlProfile_CtorFromXDocument_Works() public void XmpProfile_CtorFromXDocument_Works()
{ {
// arrange // arrange
XDocument document = CreateMinimalXDocument(); XDocument document = CreateMinimalXDocument();

Loading…
Cancel
Save