diff --git a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs index de34a1eaa..639f09722 100644 --- a/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/XMP/XmpProfile.cs @@ -46,7 +46,7 @@ public sealed class XmpProfile : IDeepCloneable /// /// Convert the content of this into an . /// - /// The + /// The instance, or if no XMP data is present. public XDocument? ToXDocument() { byte[]? data = this.Data; @@ -74,8 +74,14 @@ public sealed class XmpProfile : IDeepCloneable /// The public byte[] ToByteArray() { - Guard.NotNull(this.Data); - byte[] result = new byte[this.Data.Length]; + byte[]? data = this.Data; + + if (data is null) + { + return []; + } + + byte[] result = new byte[data.Length]; this.Data.AsSpan().CopyTo(result); return result; } @@ -83,10 +89,15 @@ public sealed class XmpProfile : IDeepCloneable /// 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]; - this.Data.AsSpan().CopyTo(clone); + byte[] clone = new byte[data.Length]; + data.AsSpan().CopyTo(clone); return new XmpProfile(clone); } @@ -118,7 +129,14 @@ public sealed class XmpProfile : IDeepCloneable } // 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; + + // 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; if (!hasBom && !hasTrailingPad) @@ -146,7 +164,7 @@ public sealed class XmpProfile : IDeepCloneable int length = end - start; if (length <= 0) { - return []; + return null; } byte[] normalized = new byte[length]; diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs index 8e8f89e6f..32d4bc7cd 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/XMP/XmpProfileTests.cs @@ -79,7 +79,7 @@ public class XmpProfileTests } [Fact] - public void XmlProfile_CtorFromXDocument_Works() + public void XmpProfile_CtorFromXDocument_Works() { // arrange XDocument document = CreateMinimalXDocument();