6
6
using System . Collections . Generic ;
7
7
using System . Collections . ObjectModel ;
8
8
using System . Text ;
9
+ using SixLabors . ImageSharp . Metadata . Profiles . IPTC ;
9
10
10
11
namespace SixLabors . ImageSharp . Metadata . Profiles . Iptc
11
12
{
@@ -20,6 +21,11 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
20
21
21
22
private const uint MaxStandardDataTagSize = 0x7FFF ;
22
23
24
+ /// <summary>
25
+ /// 1:90 Coded Character Set.
26
+ /// </summary>
27
+ private const byte IptcEnvelopeCodedCharacterSet = 0x5A ;
28
+
23
29
/// <summary>
24
30
/// Initializes a new instance of the <see cref="IptcProfile"/> class.
25
31
/// </summary>
@@ -64,6 +70,11 @@ private IptcProfile(IptcProfile other)
64
70
}
65
71
}
66
72
73
+ /// <summary>
74
+ /// Gets a byte array marking that UTF-8 encoding is used in application records.
75
+ /// </summary>
76
+ private static ReadOnlySpan < byte > CodedCharacterSetUtf8Value => new byte [ ] { 0x1B , 0x25 , 0x47 } ; // Uses C#'s optimization to refer to the data segment in the assembly directly, no allocation occurs.
77
+
67
78
/// <summary>
68
79
/// Gets the byte data of the IPTC profile.
69
80
/// </summary>
@@ -194,6 +205,17 @@ public void SetValue(IptcTag tag, Encoding encoding, string value, bool strict =
194
205
this . values . Add ( new IptcValue ( tag , encoding , value , strict ) ) ;
195
206
}
196
207
208
+ /// <summary>
209
+ /// Sets the value of the specified tag.
210
+ /// </summary>
211
+ /// <param name="tag">The tag of the iptc value.</param>
212
+ /// <param name="value">The value.</param>
213
+ /// <param name="strict">
214
+ /// Indicates if length restrictions from the specification should be followed strictly.
215
+ /// Defaults to true.
216
+ /// </param>
217
+ public void SetValue ( IptcTag tag , string value , bool strict = true ) => this . SetValue ( tag , Encoding . UTF8 , value , strict ) ;
218
+
197
219
/// <summary>
198
220
/// Makes sure the datetime is formatted according to the iptc specification.
199
221
/// <example>
@@ -219,17 +241,6 @@ public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset)
219
241
this . SetValue ( tag , Encoding . UTF8 , formattedDate ) ;
220
242
}
221
243
222
- /// <summary>
223
- /// Sets the value of the specified tag.
224
- /// </summary>
225
- /// <param name="tag">The tag of the iptc value.</param>
226
- /// <param name="value">The value.</param>
227
- /// <param name="strict">
228
- /// Indicates if length restrictions from the specification should be followed strictly.
229
- /// Defaults to true.
230
- /// </param>
231
- public void SetValue ( IptcTag tag , string value , bool strict = true ) => this . SetValue ( tag , Encoding . UTF8 , value , strict ) ;
232
-
233
244
/// <summary>
234
245
/// Updates the data of the profile.
235
246
/// </summary>
@@ -241,12 +252,25 @@ public void UpdateData()
241
252
length += value . Length + 5 ;
242
253
}
243
254
255
+ bool hasValuesInUtf8 = this . HasValuesInUtf8 ( ) ;
256
+
257
+ if ( hasValuesInUtf8 )
258
+ {
259
+ // Additional length for UTF-8 Tag.
260
+ length += 5 + CodedCharacterSetUtf8Value . Length ;
261
+ }
262
+
244
263
this . Data = new byte [ length ] ;
264
+ int offset = 0 ;
265
+ if ( hasValuesInUtf8 )
266
+ {
267
+ // Write Envelope Record.
268
+ offset = this . WriteRecord ( offset , CodedCharacterSetUtf8Value , IptcRecordNumber . Envelope , IptcEnvelopeCodedCharacterSet ) ;
269
+ }
245
270
246
- int i = 0 ;
247
271
foreach ( IptcValue value in this . Values )
248
272
{
249
- // Standard DataSet Tag
273
+ // Write Application Record.
250
274
// +-----------+----------------+---------------------------------------------------------------------------------+
251
275
// | Octet Pos | Name | Description |
252
276
// +==========-+================+=================================================================================+
@@ -263,17 +287,26 @@ public void UpdateData()
263
287
// | | Octet Count | the following data field(32767 or fewer octets). Note that the value of bit 7 of|
264
288
// | | | octet 4(most significant bit) always will be 0. |
265
289
// +-----------+----------------+---------------------------------------------------------------------------------+
266
- this . Data [ i ++ ] = IptcTagMarkerByte ;
267
- this . Data [ i ++ ] = 2 ;
268
- this . Data [ i ++ ] = ( byte ) value . Tag ;
269
- this . Data [ i ++ ] = ( byte ) ( value . Length >> 8 ) ;
270
- this . Data [ i ++ ] = ( byte ) value . Length ;
271
- if ( value . Length > 0 )
272
- {
273
- Buffer . BlockCopy ( value . ToByteArray ( ) , 0 , this . Data , i , value . Length ) ;
274
- i += value . Length ;
275
- }
290
+ offset = this . WriteRecord ( offset , value . ToByteArray ( ) , IptcRecordNumber . Application , ( byte ) value . Tag ) ;
291
+ }
292
+ }
293
+
294
+ private int WriteRecord ( int offset , ReadOnlySpan < byte > recordData , IptcRecordNumber recordNumber , byte recordBinaryRepresentation )
295
+ {
296
+ Span < byte > data = this . Data . AsSpan ( offset , 5 ) ;
297
+ data [ 0 ] = IptcTagMarkerByte ;
298
+ data [ 1 ] = ( byte ) recordNumber ;
299
+ data [ 2 ] = recordBinaryRepresentation ;
300
+ data [ 3 ] = ( byte ) ( recordData . Length >> 8 ) ;
301
+ data [ 4 ] = ( byte ) recordData . Length ;
302
+ offset += 5 ;
303
+ if ( recordData . Length > 0 )
304
+ {
305
+ recordData . CopyTo ( this . Data . AsSpan ( offset ) ) ;
306
+ offset += recordData . Length ;
276
307
}
308
+
309
+ return offset ;
277
310
}
278
311
279
312
private void Initialize ( )
@@ -298,6 +331,7 @@ private void Initialize()
298
331
bool isValidRecordNumber = recordNumber is >= 1 and <= 9 ;
299
332
var tag = ( IptcTag ) this . Data [ offset ++ ] ;
300
333
bool isValidEntry = isValidTagMarker && isValidRecordNumber ;
334
+ bool isApplicationRecord = recordNumber == ( byte ) IptcRecordNumber . Application ;
301
335
302
336
uint byteCount = BinaryPrimitives . ReadUInt16BigEndian ( this . Data . AsSpan ( offset , 2 ) ) ;
303
337
offset += 2 ;
@@ -307,15 +341,32 @@ private void Initialize()
307
341
break ;
308
342
}
309
343
310
- if ( isValidEntry && byteCount > 0 && ( offset <= this . Data . Length - byteCount ) )
344
+ if ( isValidEntry && isApplicationRecord && byteCount > 0 && ( offset <= this . Data . Length - byteCount ) )
311
345
{
312
- var iptcData = new byte [ byteCount ] ;
346
+ byte [ ] iptcData = new byte [ byteCount ] ;
313
347
Buffer . BlockCopy ( this . Data , offset , iptcData , 0 , ( int ) byteCount ) ;
314
348
this . values . Add ( new IptcValue ( tag , iptcData , false ) ) ;
315
349
}
316
350
317
351
offset += ( int ) byteCount ;
318
352
}
319
353
}
354
+
355
+ /// <summary>
356
+ /// Gets if any value has UTF-8 encoding.
357
+ /// </summary>
358
+ /// <returns>true if any value has UTF-8 encoding.</returns>
359
+ private bool HasValuesInUtf8 ( )
360
+ {
361
+ foreach ( IptcValue value in this . values )
362
+ {
363
+ if ( value . Encoding == Encoding . UTF8 )
364
+ {
365
+ return true ;
366
+ }
367
+ }
368
+
369
+ return false ;
370
+ }
320
371
}
321
372
}
0 commit comments