Skip to content

Commit 81d533b

Browse files
Improve behavior and add comments
1 parent bc35ee6 commit 81d533b

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public XmpProfile(XDocument document)
4646
/// <summary>
4747
/// Convert the content of this <see cref="XmpProfile"/> into an <see cref="XDocument"/>.
4848
/// </summary>
49-
/// <returns>The <see cref="XDocument"/></returns>
49+
/// <returns>The <see cref="XDocument"/> instance, or <see langword="null"/> if no XMP data is present.</returns>
5050
public XDocument? ToXDocument()
5151
{
5252
byte[]? data = this.Data;
@@ -74,19 +74,30 @@ public XmpProfile(XDocument document)
7474
/// <returns>The <see cref="T:Byte[]"/></returns>
7575
public byte[] ToByteArray()
7676
{
77-
Guard.NotNull(this.Data);
78-
byte[] result = new byte[this.Data.Length];
77+
byte[]? data = this.Data;
78+
79+
if (data is null)
80+
{
81+
return [];
82+
}
83+
84+
byte[] result = new byte[data.Length];
7985
this.Data.AsSpan().CopyTo(result);
8086
return result;
8187
}
8288

8389
/// <inheritdoc/>
8490
public XmpProfile DeepClone()
8591
{
86-
Guard.NotNull(this.Data);
92+
byte[]? data = this.Data;
93+
if (data is null)
94+
{
95+
// Preserve the semantics of an "empty" profile when cloning.
96+
return new XmpProfile();
97+
}
8798

88-
byte[] clone = new byte[this.Data.Length];
89-
this.Data.AsSpan().CopyTo(clone);
99+
byte[] clone = new byte[data.Length];
100+
data.AsSpan().CopyTo(clone);
90101
return new XmpProfile(clone);
91102
}
92103

@@ -118,7 +129,14 @@ private static byte[] SerializeDocument(XDocument document)
118129
}
119130

120131
// Allocation-free fast path for the normal case.
132+
133+
// Check for UTF-8 BOM (0xEF,0xBB,0xBF)
121134
bool hasBom = data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF;
135+
136+
// XMP metadata is commonly stored in fixed-size container blocks (e.g. TIFF tag 700).
137+
// Producers often pad unused space so the packet can be updated in-place without
138+
// rewriting the file. In practice this padding is either NUL (0x00) from the container
139+
// or 0x0F used by Adobe XMP writers. Both are invalid XML and must be trimmed.
122140
bool hasTrailingPad = data[^1] is 0 or 0x0F;
123141

124142
if (!hasBom && !hasTrailingPad)
@@ -146,7 +164,7 @@ private static byte[] SerializeDocument(XDocument document)
146164
int length = end - start;
147165
if (length <= 0)
148166
{
149-
return [];
167+
return null;
150168
}
151169

152170
byte[] normalized = new byte[length];

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public async Task ReadXmpMetadata_FromWebp_Works<TPixel>(TestImageProvider<TPixe
7979
}
8080

8181
[Fact]
82-
public void XmlProfile_CtorFromXDocument_Works()
82+
public void XmpProfile_CtorFromXDocument_Works()
8383
{
8484
// arrange
8585
XDocument document = CreateMinimalXDocument();

0 commit comments

Comments
 (0)