JPG file "Title" and "Subject" will be removed when removing other property

Hi Sir/Madam,

We find that when trying to remove the other property of JPG, but we found that “Title” and “Subject” will be removed. They should not be affected. Is it the expected behavior, or a bug of GroupDocs.Metadata library?

Here’s the file we used:
JPGfile.zip (782.3 KB)

Here’s the code we used:
FormatBase format = new JpegFormat(filePath);
if (format == null)
return;
JpegExifInfo exifInfo = (JpegExifInfo)((JpegFormat)format).ExifValues;
_exifData.RemoveMetaData(format, exifInfo, options);
XmpEditableCollection _xmpEditableCollection = ((JpegFormat)format).XmpValues;
_xmpData.RemoveMetaData(_xmpEditableCollection, options);
format.save(outPath);
_exifData.RemoveMetaData() and _xmpData.RemoveMetaData() will remove exifdata and xmpdata, if we just run:
FormatBase format = new JpegFormat(filePath);
format.save(outPath);
“Title” and “Subject” alse will be removed.

@Glority_Developer,

Thanks for using GroupDocs.Metadata and sharing your concerns with us.

Would you please share with us the source code of _exifData.RemoveMetaData() and _xmpData.RemoveMetaData() methods that are used in your code? We shall be looking forward to your response.

With this code you can reproduce this issue:
FormatBase format = new JpegFormat(filePath);
format.save(outPath);
source: IMG_2004_orginal.JPG (757.9 KB)

@Glority_Developer,

Thanks for your response.

We are unable to reproduce your reported issue (see screenshot) using the following code:

FormatBase format = new JpegFormat("IMG_2004_orginal.JPG");
format.Save("IMG_2004_orginal_output.JPG");

Furthermore, we can see that you are removing XMP metadata as well as EXIF metadata in your provided code snippet (in this post). Please note that removing EXIF metadata using JpegFormat.RemoveExifInfo() method removes all the EXIF info including Title and Subject fields. Hence, removal of Title and Subject fields’ values in your scenario is expected behavior of the API. In case you would have any confusion or question, please feel free to let us know.

    XmpEditableCollection _xmpEditableCollection = ((JpegFormat)format).XmpValues;
    _xmpData.RemoveMetaData(_xmpEditableCollection, options);
    #region remove XMP metadata
    public void RemoveMetaData(XmpEditableCollection xmpEditableCollection, RemoveOptions options)
    {
        if (xmpEditableCollection == null)
        {
            return;
        }

        XmpSchemes schemes = xmpEditableCollection.Schemes;
        if (schemes == null)
        {
            return;
        }

        RemoveXmpDCData(schemes, options.XmpDCData);
        RemoveXmpPDFData(schemes, options.XmpPDFData);
        RemoveXmpPSData(schemes, options.XmpPSData);
        RemoveXmpXBData(schemes, options.XmpXBData);
    }
    #endregion

    #region remove XMP DublinCorePackage metadata
    private void RemoveXmpDCData(XmpSchemes schemes, List<int> xmpDCData)
    {
        for (int i = 0; i < xmpDCData.Count; ++i)
        {
            XMPDCENUM data = (XMPDCENUM)xmpDCData[i];
            switch (data)
            {
                case XMPDCENUM.Contributors:
                    {
                        if (schemes.DublinCore.Contributors != null && schemes.DublinCore.Contributors.Length != 0)
                        {
                            schemes.DublinCore.Contributors = new string[] { };
                        }
                        break;
                    }
                case XMPDCENUM.Creators:
                    {
                        if (schemes.DublinCore.Creators != null && schemes.DublinCore.Creators.Length != 0)
                        {
                            schemes.DublinCore.Creators = new string[] { };
                        }
                        break;
                    }
                case XMPDCENUM.Source:
                    {
                        if (!string.IsNullOrEmpty(schemes.DublinCore.Source))
                        {
                            schemes.DublinCore.Source = string.Empty;
                        }
                        break;
                    }
                case XMPDCENUM.Subject:
                    {
                        if (!string.IsNullOrEmpty(schemes.DublinCore.Subject))
                        {
                            schemes.DublinCore.Subject = string.Empty;
                        }
                        break;
                    }
                default:
                    break;
            }
        }
    }
    #endregion

    #region remove XMP PdfPackage metadata
    private void RemoveXmpPDFData(XmpSchemes schemes, List<int> xmpPDFData)
    {
        for (int i = 0; i < xmpPDFData.Count; ++i)
        {
            XMPPDFENUM data = (XMPPDFENUM)xmpPDFData[i];
            switch (data)
            {
                case XMPPDFENUM.Keywords:
                    {
                        if (!string.IsNullOrEmpty(schemes.Pdf.Keywords))
                        {
                            schemes.Pdf.Keywords = string.Empty;
                        }
                        break;
                    }
                case XMPPDFENUM.Producer:
                    {
                        if (!string.IsNullOrEmpty(schemes.Pdf.Producer))
                        {
                            schemes.Pdf.Producer = string.Empty;
                        }
                        break;
                    }
                default:
                    break;
            }
        }
    }
    #endregion

    #region remove XMP PhotoshopPackage metadata
    private void RemoveXmpPSData(XmpSchemes schemes, List<int> xmpPSData)
    {
        for (int i = 0; i < xmpPSData.Count; ++i)
        {
            XMPPSENUM data = (XMPPSENUM)xmpPSData[i];
            switch (data)
            {
                case XMPPSENUM.AuthorsPosition:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.AuthorsPosition))
                        {
                            schemes.Photoshop.AuthorsPosition = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.CaptionWriter:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.CaptionWriter))
                        {
                            schemes.Photoshop.CaptionWriter = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.City:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.City))
                        {
                            schemes.Photoshop.City = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.Country:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.Country))
                        {
                            schemes.Photoshop.Country = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.Credit:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.Credit))
                        {
                            schemes.Photoshop.Credit = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.DateCreated:
                    {
                        if (schemes.Photoshop.DateCreated != DateTime.MinValue)
                        {
                            schemes.Photoshop.DateCreated = DateTime.MinValue;
                        }
                        break;
                    }
                case XMPPSENUM.Headline:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.Headline))
                        {
                            schemes.Photoshop.Headline = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.History:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.History))
                        {
                            schemes.Photoshop.History = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.Instructions:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.Instructions))
                        {
                            schemes.Photoshop.Instructions = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.Source:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.Source))
                        {
                            schemes.Photoshop.Source = string.Empty;
                        }
                        break;
                    }
                case XMPPSENUM.State:
                    {
                        if (!string.IsNullOrEmpty(schemes.Photoshop.State))
                        {
                            schemes.Photoshop.State = string.Empty;
                        }
                        break;
                    }
                default:
                    break;
            }
        }
    }
    #endregion

    #region remove XMP XmpBasicPackage metadata
    private void RemoveXmpXBData(XmpSchemes schemes, List<int> xmpXBData)
    {
        for (int i = 0; i < xmpXBData.Count; ++i)
        {
            XMPXBENUM data = (XMPXBENUM)xmpXBData[i];
            switch (data)
            {
                case XMPXBENUM.BaseUrl:
                    {
                        if (!string.IsNullOrEmpty(schemes.XmpBasic.BaseUrl))
                        {
                            schemes.XmpBasic.BaseUrl = string.Empty;
                        }
                        break;
                    }
                case XMPXBENUM.CreateDate:
                    {
                        if (schemes.XmpBasic.CreateDate != DateTime.MinValue)
                        {
                            schemes.XmpBasic.CreateDate = DateTime.MinValue;
                        }
                        break;
                    }
                case XMPXBENUM.CreatorTool:
                    {
                        if (!string.IsNullOrEmpty(schemes.XmpBasic.CreatorTool))
                        {
                            schemes.XmpBasic.CreatorTool = string.Empty;
                        }
                        break;
                    }
                case XMPXBENUM.Label:
                    {
                        if (!string.IsNullOrEmpty(schemes.XmpBasic.Label))
                        {
                            schemes.XmpBasic.Label = string.Empty;
                        }
                        break;
                    }
                case XMPXBENUM.MetadataDate:
                    {
                        if (schemes.XmpBasic.MetadataDate != DateTime.MinValue)
                        {
                            schemes.XmpBasic.MetadataDate = DateTime.MinValue;
                        }
                        break;
                    }
                case XMPXBENUM.ModifyDate:
                    {
                        if (schemes.XmpBasic.ModifyDate != DateTime.MinValue)
                        {
                            schemes.XmpBasic.ModifyDate = DateTime.MinValue;
                        }
                        break;
                    }
                case XMPXBENUM.Nickname:
                    {
                        if (!string.IsNullOrEmpty(schemes.XmpBasic.Nickname))
                        {
                            schemes.XmpBasic.Nickname = string.Empty;
                        }
                        break;
                    }

                default:
                    break;
            }
        }
    }
    #endregion

    JpegExifInfo exifInfo = (JpegExifInfo)((JpegFormat)format).ExifValues;
    _exifData.RemoveMetaData(format, exifInfo, options);
    #region remove exif metadata
    public void RemoveMetaData(FormatBase format, ExifInfo exifInfo, RemoveOptions options)
    {
        if (format == null || exifInfo == null)
        {
            return;
        }

        for (int i = 0; i < options.ExifData.Count; ++i)
        {
            EXIFENUM data = (EXIFENUM)options.ExifData[i];
            switch (data)
            {
                case EXIFENUM.JEI_Artist:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).Artist))
                        {
                            ((JpegExifInfo)exifInfo).Artist = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.JEI_DateTime:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).DateTime))
                        {
                            ((JpegExifInfo)exifInfo).DateTime = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.JEI_ImageDescription:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).ImageDescription))
                        {
                            ((JpegExifInfo)exifInfo).ImageDescription = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.JEI_Make:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).Make))
                        {
                            ((JpegExifInfo)exifInfo).Make = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.JEI_Model:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).Model))
                        {
                            ((JpegExifInfo)exifInfo).Model = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.JEI_Software:
                    {
                        if (exifInfo is JpegExifInfo && !string.IsNullOrEmpty(((JpegExifInfo)exifInfo).Software))
                        {
                            ((JpegExifInfo)exifInfo).Software = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.BodySerialNumber:
                    {
                        if (!string.IsNullOrEmpty(exifInfo.BodySerialNumber))
                        {
                            exifInfo.BodySerialNumber = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.CameraOwnerName:
                    {
                        if (!string.IsNullOrEmpty(exifInfo.CameraOwnerName))
                        {
                            exifInfo.CameraOwnerName = string.Empty;
                        }
                        break;
                    }
                case EXIFENUM.GPSData:
                    {
                        ((IExif)format).UpdateExifInfo(exifInfo);
                        ((IExif)format).RemoveGpsLocation();
                        exifInfo = ((IExif)format).ExifValues;
                        break;
                    }
                case EXIFENUM.UserComment:
                    {
                        if (!string.IsNullOrEmpty(exifInfo.UserComment))
                        {
                            exifInfo.UserComment = string.Empty;
                        }
                        break;
                    }
                default:
                    break;
            }
        }

        ((IExif)format).UpdateExifInfo(exifInfo);
    }
    #endregion

@Glority_Developer,

Thanks for providing the complete source code.

We are able to reproduce your reported issue using the complete source code. We noticed that Title and Subject fields are updated when we update the value of JpegExifInfo.ImageDescription property. We have logged this issue in our internal Issue Tracking System as METADATANET-2421 for further investigation. We shall keep you notified in case of any updates.

@Glority_Developer,

We have got the results of the investigation about the issue logged as METADATANET-2421.

First of all, to understand a reason for such behavior, let’s have a closer look at the official EXIF documentation: http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf. As you can see, it doesn’t contain any mentions of the Title or Subject tags. There is only one EXIF property containing a textual description of an image that is called ImageDescription. Please note that the ImageDescription tag has the ID equal to 270.

Now, let’s try to find out what the Title and Subject properties are. Most likely, all metadata values that you see in the file’s Properties dialog are provided by the Windows Imaging Component (Windows Imaging Component - Win32 apps | Microsoft Learn) which is a built-in Windows component allowing low-level access to digital images. The WIC documentation describes two very familiar metadata properties:

Please compare the sources from which the WIC reads values for the above-mentioned properties (see the “JPEG Policy” → “Read Paths” paragraph). You’ll find that there is one mutual source path for them - /app1/ifd/{ushort=270}. The path is read like this:

  • app1 - JPEG marker intended to store metadata (including EXIF)
  • ifd means Image File Directory - the main building block of the EXIF format
  • ushort=270 - the ID of the EXIF tag from which the property value is read

As we already know, 270 is the ID of the ImageDescription tag. This means that in some cases both fields (Title and Subject) can display the same EXIF property value as that of ImageDescription. Hence, removing or replacing the value of ImageDescription may remove/replace the value of Title and Subject as well and it is not the bug.

Please feel free to let us know in case of any confusion.

@usman.aziz

Thanks for your detailed information. But when we try to remove Camera maker and Camera model, the Title and Subject will also be removed.
image.jpg (106.9 KB)
image.jpg (115.3 KB)
Notes: you can get the test file in the JPGfile.zip in post 1.
This doesn’t make sense. I think ImageDescription isn’t related with Camera maker and Camera model, so Title and Subject shouldn’t be affected.

Would you please help check it again? Thanks.

@Glority_Developer,

Thanks for coming back. We are currently investigating your scenario, we shall update you shortly.

@Glority_Developer,

Your reported issue was reproduced using GroupDocs.Metadata for .NET 18.7 and the bug is fixed in the latest version 18.8 of the API. Please feel free to install the latest version of API.

We try version 18.8, with code:
FormatBase format = new JpegFormat(filePath);
ExifInfo exifInfo = ((JpegFormat)format).GetExifInfo();
((JpegFormat)format).UpdateExifInfo(exifInfo);
it will throw a exception, info: Could not update metadata. Evaluation only. Created with GroupDocs.Metadata for .NET. Copyright 2018 Aspose Pty Ltd.

@Glority_Developer,

Thanks for using GroupDocs.Metadata for .NET 18.8.

Evaluation message appears only when the license is not applied or it is applied but expired. Please make sure that you are applying license before updating metadata.

If the evaluation message started appearing only after upgrading to version 18.8, then possibly your license is expired and you are not eligible for updates. Please open your license file in any text editor and share with us the subscription expiry date.

For some JPG files, we found that when I choose to remove just the “Title”, we found the output files will show the “Title” and “Subject” fields have the same exact data, which is the data from the Title field.

Here’re the JPG files:
Img0002.jpeg (294.8 KB)
Img0001.jpeg (246.6 KB)

@Glority_Developer,

Thanks for coming back to us. With the files attached, we are unable to reproduce your reported issue at our end. Please share both input and output files so that we can further investigate this issue at our end.

@ali.ahmed

Please download the input and output JPG files here:
jpg files.zip (683.7 KB)

@Glority_Developer,

Thanks for providing the sample files. We investigated your issue and were unable to reproduce it at our end. Based on your scenario please find this sample console application and provide us your feedback.
GroupDocs.Metadata.Sample.zip (1.4 MB)

We have tried your sample codes, it doesn’t work for us.

Here’s the demo codes in our side: (we are using v18.6)
var format = new JpegFormat(filePath);
if (format == null)

{ return; }

((JpegFormat)format).XmpValues.Schemes.DublinCore.SetTitle("");
//JpegExifInfo exifInfo = (JpegExifInfo)((JpegFormat)format).ExifValues;
//((IExif)format).UpdateExifInfo(exifInfo);
format.Save(outPath);

((JpegFormat)format).XmpValues.Schemes.DublinCore.SetTitle(""); can’tr delete JPG Title.
If we use UpdateExifInfo, the properties of JPG files will be changed, and subject will be changed to Title.

@Glority_Developer,

Thanks for coming back.

Your reported issues are reproduced using GroupDocs.Metadata for .NET 18.6 and they are fixed in the latest version 18.8. Using the latest version, please see the results here. Results.png (19.3 KB)

Please feel free to install the latest version of API and provide us with your feedback.