-
-
Notifications
You must be signed in to change notification settings - Fork 887
Fix IPTC tags written on jpg files that contains non-English characters can't be correctly displayed on external apps #2212 #2213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
9c7ba12
a58d8d5
e314305
65c3c44
288fb0a
41bef5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |||||
| using System.Collections.Generic; | ||||||
| using System.Collections.ObjectModel; | ||||||
| using System.Text; | ||||||
| using SixLabors.ImageSharp.Metadata.Profiles.IPTC; | ||||||
|
|
||||||
| namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc | ||||||
| { | ||||||
|
|
@@ -20,6 +21,16 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile> | |||||
|
|
||||||
| private const uint MaxStandardDataTagSize = 0x7FFF; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// 1:90 Coded Character Set. | ||||||
| /// </summary> | ||||||
| private const byte IptcEnvelopeCodedCharacterSet = 0x5A; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// This value marks that UTF-8 encoding is used in application records. | ||||||
| /// </summary> | ||||||
| private static readonly byte[] CodedCharacterSetUtf8Value = { 0x1B, 0x25, 0x47 }; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Initializes a new instance of the <see cref="IptcProfile"/> class. | ||||||
| /// </summary> | ||||||
|
|
@@ -194,6 +205,17 @@ public void SetValue(IptcTag tag, Encoding encoding, string value, bool strict = | |||||
| this.values.Add(new IptcValue(tag, encoding, value, strict)); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Sets the value of the specified tag. | ||||||
| /// </summary> | ||||||
| /// <param name="tag">The tag of the iptc value.</param> | ||||||
| /// <param name="value">The value.</param> | ||||||
| /// <param name="strict"> | ||||||
| /// Indicates if length restrictions from the specification should be followed strictly. | ||||||
| /// Defaults to true. | ||||||
| /// </param> | ||||||
| public void SetValue(IptcTag tag, string value, bool strict = true) => this.SetValue(tag, Encoding.UTF8, value, strict); | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Makes sure the datetime is formatted according to the iptc specification. | ||||||
| /// <example> | ||||||
|
|
@@ -219,17 +241,6 @@ public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset) | |||||
| this.SetValue(tag, Encoding.UTF8, formattedDate); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Sets the value of the specified tag. | ||||||
| /// </summary> | ||||||
| /// <param name="tag">The tag of the iptc value.</param> | ||||||
| /// <param name="value">The value.</param> | ||||||
| /// <param name="strict"> | ||||||
| /// Indicates if length restrictions from the specification should be followed strictly. | ||||||
| /// Defaults to true. | ||||||
| /// </param> | ||||||
| public void SetValue(IptcTag tag, string value, bool strict = true) => this.SetValue(tag, Encoding.UTF8, value, strict); | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Updates the data of the profile. | ||||||
| /// </summary> | ||||||
|
|
@@ -241,12 +252,25 @@ public void UpdateData() | |||||
| length += value.Length + 5; | ||||||
| } | ||||||
|
|
||||||
| bool hasValuesInUtf8 = this.HasValuesInUtf8(); | ||||||
|
|
||||||
| if (hasValuesInUtf8) | ||||||
| { | ||||||
| // Additional length for UTF-8 Tag. | ||||||
| length += 5 + CodedCharacterSetUtf8Value.Length; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cool thing is that with the above suggestion the JIT will treat |
||||||
| } | ||||||
|
|
||||||
| this.Data = new byte[length]; | ||||||
| int offset = 0; | ||||||
| if (hasValuesInUtf8) | ||||||
| { | ||||||
| // Write Envelope Record. | ||||||
| offset = this.WriteRecord(offset, CodedCharacterSetUtf8Value, IptcRecordNumber.Envelope, IptcEnvelopeCodedCharacterSet); | ||||||
| } | ||||||
|
|
||||||
| int i = 0; | ||||||
| foreach (IptcValue value in this.Values) | ||||||
| { | ||||||
| // Standard DataSet Tag | ||||||
| // Write Application Record. | ||||||
| // +-----------+----------------+---------------------------------------------------------------------------------+ | ||||||
| // | Octet Pos | Name | Description | | ||||||
| // +==========-+================+=================================================================================+ | ||||||
|
|
@@ -263,17 +287,24 @@ public void UpdateData() | |||||
| // | | Octet Count | the following data field(32767 or fewer octets). Note that the value of bit 7 of| | ||||||
| // | | | octet 4(most significant bit) always will be 0. | | ||||||
| // +-----------+----------------+---------------------------------------------------------------------------------+ | ||||||
| this.Data[i++] = IptcTagMarkerByte; | ||||||
| this.Data[i++] = 2; | ||||||
| this.Data[i++] = (byte)value.Tag; | ||||||
| this.Data[i++] = (byte)(value.Length >> 8); | ||||||
| this.Data[i++] = (byte)value.Length; | ||||||
| if (value.Length > 0) | ||||||
| { | ||||||
| Buffer.BlockCopy(value.ToByteArray(), 0, this.Data, i, value.Length); | ||||||
| i += value.Length; | ||||||
| } | ||||||
| offset = this.WriteRecord(offset, value.ToByteArray(), IptcRecordNumber.Application, (byte)value.Tag); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| private int WriteRecord(int offset, byte[] recordData, IptcRecordNumber recordNumber, byte recordBinaryRepresentation) | ||||||
| { | ||||||
| this.Data[offset++] = IptcTagMarkerByte; | ||||||
| this.Data[offset++] = (byte)recordNumber; | ||||||
| this.Data[offset++] = recordBinaryRepresentation; | ||||||
| this.Data[offset++] = (byte)(recordData.Length >> 8); | ||||||
| this.Data[offset++] = (byte)recordData.Length; | ||||||
|
||||||
| if (recordData.Length > 0) | ||||||
| { | ||||||
| Buffer.BlockCopy(recordData, 0, this.Data, offset, recordData.Length); | ||||||
|
||||||
| Buffer.BlockCopy(recordData, 0, this.Data, offset, recordData.Length); | |
| recordData.CopyTo(this.Data.AsSpan(offset)); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Six Labors Split License. | ||
|
|
||
| namespace SixLabors.ImageSharp.Metadata.Profiles.IPTC | ||
| { | ||
| /// <summary> | ||
| /// Enum for the different record types of a IPTC value. | ||
| /// </summary> | ||
| internal enum IptcRecordNumber : byte | ||
| { | ||
| /// <summary> | ||
| /// A Envelope Record. | ||
| /// </summary> | ||
| Envelope = 0x01, | ||
|
|
||
| /// <summary> | ||
| /// A Application Record. | ||
| /// </summary> | ||
| Application = 0x02 | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And update
WriteRecord(L294) to take a ROS.