【问题标题】:Extra characters/bytes in metadata after saving a PNG image保存 PNG 图像后元数据中的额外字符/字节
【发布时间】:2021-04-20 22:20:30
【问题描述】:

我正在尝试创建要存储在 PNG 图像文件中的元数据,具体取决于名为 sc_status 的参数的值。

代码如下:

Dim qualityParam As Object
Dim encoderParams As Object = New Imaging.EncoderParameters(1)
Dim ImgCodec As Imaging.ImageCodecInfo

ImgCodec = GetEncoderInfo("image/png")
qualityParam = New Imaging.EncoderParameter(Imaging.Encoder.ColorDepth, 32L)
encoderParams.Param(0) = qualityParam

'---
' img_src Image is created here
' file_name String is created here
'---

' Creating the PropertyItem
Dim propit As Imaging.PropertyItem = CType(System.Runtime.Serialization.FormatterServices.GetUninitializedObject(GetType(Imaging.PropertyItem)), Imaging.PropertyItem)

propit.Id = 270 '0x010E = Image description
propit.Type = 2

If sc_status = 3 Then
    propit.Value = System.Text.Encoding.UTF8.GetBytes("HQ")
ElseIf sc_status = 5 Then
    propit.Value = System.Text.Encoding.UTF8.GetBytes("LQ")
Else
    propit.Value = System.Text.Encoding.UTF8.GetBytes("UQ")
End If

' Storing the PropertyItem
img_src.SetPropertyItem(propit)

' Saving png
img_src.Save(file_name, ImgCodec, encoderParams)

当我查看存储在 PNG 块中的内容时,我希望有[72, 81, 0][76, 81, 0][85, 81, 0] 的字节序列,对应于字符串"LQ","HQ",@987654328 @ 加上 vbNullChar,它会自动添加到 PNG 块的末尾。

但由于我忽略的原因,有时我的字节序列更长,例如[72, 81, 28, 8, 1, 0] 给出 - 使用 System.Text.Encoding.UTF8.GetString() 后 - 字符串:

HQ & ChrW(28) & vbBack & ChrW(1) & vbNullChar

有时[72, 81, 22, 8, 1, 0],有时[72, 81, 19, 8, 1, 0],有时[72, 81, 23, 8, 1, 0]

我不明白为什么在img_src.Save() 过程中有时会在元数据中添加额外的字节。
我究竟做错了什么?非常欢迎任何帮助!

【问题讨论】:

    标签: vb.net graphics png metadata


    【解决方案1】:

    PropertyTagImageDescription 定义为以空字符结尾的 ASCII 字符串 (PropertyTagTypeASCII)。
    如文档中所述,PropertyItem.Type PropertyTagTypeASCII:

    指定Value 是一个以null 结尾的ASCII 字符串。如果你设置 类型数据成员为 ASCII 类型,您应该将 Len 属性设置为 包括空终止符的字符串的长度。例如, 字符串"Hello" 的长度为 6。

    一些细节:

    • PropertyItem.Value 属性将 data 存储为字节数组。尽管文档引用了 ASCII 字符串,但存储 UTF-8 编码字符串的字节,调用Encoding.UTF8.GetBytes() 检索,但没有禁止。只需终止字符串并设置正确的存储字节数:如上所述 Value 仅存储字节。
    • PropertyItem.Len 属性必须设置为字符串的长度,因此设置为字节的长度,因此设置为Value 属性的长度。

    ► 没有理由将类型定义为Object,如下所示:

    Dim qualityParam As Object
    Dim encoderParams As Object = New Imaging.EncoderParameters(1)
    

    声明这些类型是什么。

    使用 UTF-8 编码字符串的示例实现:

    Imports System.Drawing.Imaging
    Imports System.Text
    
    '0x010E = Image description
    Dim imageDescriptionPropItem = &H10E
    ' 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.Png.Guid)
    If ImgCodec Is Nothing Then
        Throw New FormatException("Invalid format")
    End If
    encoderParams.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.ColorDepth, 32L)
    
    Dim imagePath = [Image Source Path]
    Dim imageDestinationPath = [Image Destination Path]
    Dim imageSource = Image.FromStream(
        New MemoryStream(File.ReadAllBytes(imageSourcePath)), False, False)
    
    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"
        Case 5
            description = "LQ"
        Case 100
            ' Test string, in Russian :)
            description = "Тестовая строка"
        Case Else
            description = "UQ"
    End Select
    
    ' Length of the string including the terminator: mandatory
    propItem.Value = Encoding.UTF8.GetBytes(description & ChrW(0))
    propItem.Len = propItem.Value.Length
    
    imageSource.SetPropertyItem(propItem)
    imageSource.Save(imageDestinationPath, ImgCodec, encoderParams)
    
     
    
    ' Load it back, to check what's what
    Dim imageEncoded = Image.FromStream(New MemoryStream(File.ReadAllBytes(imageDestinationPath)))
    Dim propItemSaved = imageEncoded.GetPropertyItem(imageDescriptionPropItem)
    Dim descr = Encoding.UTF8.GetString(propItemSaved.Value).TrimEnd(ChrW(0))
    

    【讨论】:

      猜你喜欢
      • 2020-11-17
      • 1970-01-01
      • 2014-05-03
      • 2020-04-15
      • 1970-01-01
      • 2019-04-23
      • 1970-01-01
      • 2020-09-15
      • 2021-09-26
      相关资源
      最近更新 更多