This thread is a continuation of Extra characters/bytes in metadata after saving a PNG image
But I now consider JPEG
files, not PNG
files.
I am trying to write metadata depending on the value of a parameter called 'sc_status'. I adapted Jimi's proposal that works very fine with PNG
files:
Imports System.Drawing.Imaging
Imports System.Text
'0x9286 = User Comments
Dim imageDescriptionPropItem = &H9286
' Property Type 2: null-terminated string
Dim PropertyTagTypeASCII As short = 2
Dim encoderParams As New EncoderParameters(1)
Dim ImgCodec = ImageCodecInfo.GetImageEncoders().
FirstOrDefault(Function(enc) enc.FormatID = ImageFormat.Jpeg.Guid)
If ImgCodec Is Nothing Then
Throw New FormatException("Invalid format")
End If
encoderParams.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 95L)
Dim imagePath = [Image Source Path]
Dim imageDestinationPath = [Image Destination Path]
Dim imageSourcePath = Image.FromStream(New MemoryStream(File.ReadAllBytes(imageSourcePath)))
Dim propItem As PropertyItem = DirectCast(FormatterServices.GetUninitializedObject(
GetType(PropertyItem)), PropertyItem)
propItem.Id = imageDescriptionPropItem
propItem.Type = PropertyTagTypeASCII
Dim description = String.Empty
Select Case sc_status
Case 3
description = "HQ" & ChrW(0)
Case 5
description = "LQ" & ChrW(0)
Case Else
description = "UQ" & ChrW(0)
End Select
' Length of the string including the terminator: mandatory
propItem.Value = Encoding.UTF8.GetBytes(description)
propItem.Len = propItem.Value.Length
imageSource.SetPropertyItem(propItem)
imageSource.Save(imageDestinationPath, ImgCodec, encoderParams)
The metadata check is performed with:
Dim imageEncoded = Image.FromStream(New MemoryStream(File.ReadAllBytes(imageDestinationPath)))
Dim propItemSaved = imageEncoded.GetPropertyItem(imageDescriptionPropItem)
Dim descr = Encoding.UTF8.GetString(propItemNew.Value).TrimEnd(ChrW(0))
I expect to have in byte sequence [72, 81, 0]
or [76, 81, 0]
or [85, 81, 0]
. But I get [72, 0, 81, 0, 0]
or [76, 0, 81, 0, 0]
or [85, 0, 81, 0, 0]
. So There is an extra 0 byte between each character, which provides the following strings: "L" & vbNullChar & "Q" & vbNullChar
and so on.
Why are those zeroes inserted and how to get rid of this?
Why does it work fine with PNG
images (using Image Description prop item &H10E
) and not JPEG images (using User Comment prop item &H9286
)?
Many thanks in advance for your support.
You're setting the wrong PropertyItem.Type.
The PropertyTagExifUserComment (0x9286
) defines its Type as PropertyTagTypeUndefined = 7
, while you're setting it to PropertyTagTypeASCII = 2
, as the Type related to the PropertyTagImageDescription
PropertyItem. These Types don't store the data in the same way:
The character code used in the PropertyTagExifUserComment tag is identified based on an ID code in a fixed 8-byte area at the start of the tag data area. The unused portion of the area is padded with null characters (0). ID codes are assigned by means of registration. Because the type is not ASCII, it is not necessary to use a NULL terminator.
There's no need to set the null-terminator, but if you do, that's not a problem either.
Unfortunately, the Docs are not straightforward in relation to the PropertyItem.Type
values. These are defined in gdiplusimaging.h
. Use the PropertyItem.Type descriptions as reference.
Let's define an enumerator for these PropertyItems Types, to simplify its use:
Public Enum PropertyItemType As Short
PropertyTagTypeByte = 1
PropertyTagTypeASCII = 2
PropertyTagTypeShort = 3
PropertyTagTypeLong = 4
PropertyTagTypeRational = 5
PropertyTagTypeUndefined = 7
PropertyTagTypeSLONG = 9
PropertyTagTypeSRational = 10
End Enum
Thus the code changes in:
Dim propTagExifUserComment = &H9286
propItem.Id = propTagExifUserComment
propItem.Type = PropertyItemType.PropertyTagTypeUndefined
' [...]
Dim propItemSaved = imageSaved.GetPropertyItem(propTagExifUserComment)
Expect this, the code remains the same.