Working with Markers

Document Map

General Information

Handling Markers

Handling Tags and Comments

Task: Determine if a certain file format supports metadata

Task: Read a certain tag or comment by ID from a TIFF or EXIF file

Task: Write a certain tag or comment to a TIFF or EXIF file

Task: Read all tags and comments found in a TIFF or EXIF file without loading the image

Task: Update comments or tags in a TIFF or EXIF file without loading the image

Task: Delete a tag by ID from a file without loading it

Task: Read or write the file stamp

General Information

"Metadata" refers to extra information that is stored with an image, but is not necessary for decoding the image. It can contain information about the camera used to take the image, the author, date and time the image was taken, where the image was taken, whether the camera has GPS capabilities, etc. When an image is modified, you should preserve the original information, if possible. Metadata includes markers, tags and comments. This topic will deal primarily with markers. For more information on tags and comments, refer to the following:

Implementing TIFF Comments and Tags

Types of File Comments

JPEG files contain metadata information in user-defined markers. The user-defined markers are in the range 0xE0 to 0xFE. These markers can have a maximum size of 0xFFFD, or 65533 bytes. The markers below 0xE0 are reserved for the encoding of the image and inserting a reserved marker can make a file invalid. Therefore, you should not use reserved markers unless you are well acquainted with markers and image encoding in JPEG files.

TIFF files do not contain markers, however, LEADTOOLS provides a "workaround" for transferring Exif comments from JPEG files to TIFF files, and vice versa. The Exif metadata information from a TIFF file will be loaded as an APP1 marker. Therefore, you can load Exif metadata from an uncompressed file and store it in a compressed Exif file. You can also take metadata information from a compressed Exif file and store it in an uncompressed Exif file. Please note however, that some information cannot be stored inside uncompressed Exif files. For example, audio data is stored in APP2 markers and there is no built-in support for saving APP2 markers in TIFF files. However, you can still save the audio data yourself, using a custom tag. For more information on saving custom tags, refer to Implementing TIFF Comments and Tags.

Markers (as opposed to tags and comments) are generic data types that contain all the file tags, comments and geo keys of a file. Generally, when working on an image tag or comment, the user must have prior knowledge of the metadata type and expected values. Markers are useful when dealing with image metadata without requiring any previous knowledge.

Handling Markers

LEADTOOLS provides several functions for loading, creating, setting, modifying, getting information from and freeing markers.

To determine if a particular raster file format supports markers, call L_MarkersSupported function.

To load markers from an existing JPEG file, call L_LoadMarkers. To enumerate markers from an existing JPEG file, call L_EnumMarkers. Although TIFF files do not support markers, LEADTOOLS provides limited support through the use of APP1 Exif markers. When you call L_LoadMarkers for a TIFF file, an APP1 marker is created and all the TIFF, GPS and Exif comments will be stored in it. In addition, any other tag with an ID greater than 0x8000 (32768) will be stored in the APP1 marker. The other information will be considered as being useful only for the image and will not be loaded into the APP1 marker. The only exception is the resolution information, which will be loaded in the APP1 marker.

The L_CreateMarkers function lets you create a collection of markers from scratch. You can then insert markers with L_InsertMarker. After you call L_InsertMarker once, you can call L_EnumMarkers and insert markers using the LEADMARKERCALLBACK callback.

Markers, like tags and comments can be set so that when a file is saved using the L_SaveXXX functions, the markers, tags and/or comments that have been set will be saved too. Set the markers to save using the L_SetMarkers function. Once markers have been set using L_SetMarkers, all the save operations from the current thread will use these markers. If you do not want to save any markers, call L_SetMarkers with the hMarkers parameter set to NULL. To get the markers that were set by the last call to L_SetMarkers, call L_GetMarkers.

Please note that both the L_SetMarkers and L_GetMarkers functions use copies of the list of markers. Therefore, the resources associated with these lists of markers should be freed by calling L_FreeMarkers when the markers are no longer needed.

Markers set using L_SetMarkers can also be written to files using the L_WriteFileComment and L_WriteFileTag functions. The comments or tags being written by these two functions will overwrite any similar comments or tags contained in the markers. The L_WriteFileMetaData function can also be used to write markers, tags and comments to a file. To summarize:

 

This function:

will write this type of data

L_WriteFileComment

Comments and Markers

L_WriteFileCommentExt

Comments and Markers

L_WriteFileTag

Tags and Markers

L_WriteFileMetaData

Comments, Tags and Markers

 

Markers are generally modified by deleting all markers of a certain type. To delete all markers of a certain type, use the L_DeleteMarker function. Currently there is no function for deleting a marker with a certain index because markers are not to be manipulated like an array. When you have more than one marker of the same type, it is because the markers are storing more than 64K of data. For example, if you have multiple APP2 markers in a collection, deleting just one of those APP2 markers will invalidate all the APP2 markers in that collection. If a marker MUST be deleted using it index, call L_EnumMarkers and delete the specific marker or create a new marker collection using L_CreateMarkers and insert all markers except the marker to be deleted.

The L_EnumMarkers function can be used to enumerate and modify a marker collection. It is a powerful function, used internally by most marker manipulation functions. For each marker enumerated by L_EnumMarkers, an ENUMMARKERSCALLBACK function is called. This callback function lets you delete markers from or insert markers to the marker collection being enumerated. This allows you to modify the marker collection passed to the L_EnumMarkers function. To insert a new marker in a marker collection, use L_InsertMarker. You can get a marker by index using the L_GetMarker function.

To determine how many markers are in a collection, call L_GetMarkerCount.

Finally, to create a copy of a marker collection and obtain a handle to the new marker collection, use L_CopyMarkers.

Handling Tags and Comments

LEADTOOLS has extensive support to query/load/save the markers such as file tags, comments fields and Geo keys (additional tags found in GeoTIFF files).

The following section lists common tasks performed when working with image markers as well as the LEADTOOLS functions to use.

Task: Determine if a certain file format supports metadata

LEADTOOLS uses defined constants to denote an image file format. You can obtain the format of a file by using the Format member of the FILEINFO structure after calling L_FileInfo. When you use any of the L_LoadXXX functions, the image format of the original source image will be stored in the OriginalFormat member of the BITMAPHANDLE.

To determine if a particular file format supports the type of metadata required, use one of the following:

Function

Support

L_TagsSupported

Determines if the format supports tags. TIFF and EXIF are some of the common File formats that support tags.

L_CommentsSupported

Determines if the format supports comment fields. TIFF, EXIF, DICOM and ICA are some of the common file formats that support comment fields.

L_GeoKeysSupported

Determines if the format supports Geo key tags. GeoTIFF is the only file format that currently supports Geo key tags.

Many of the LEADTOOLS metadata query, read and write functions will return anerror if the file format does not support metadata. For example, trying to call L_ReadFileTag on a BMP file will almost certainly return ERROR_FEATURE_NOT_SUPPORTED. To eliminate the need to check and process these error values and speed up your code, you can use any of the functions above and only call the corresponding query, read or write metadata function if the format supports it.

For example, the following code will read the Description comment field from a disk image file. If the file supports comments, the value will be read and parsed, otherwise, it will display a message:

L_VOID ReadDescriptionComment (L_TCHAR * pszFileName)
{
   // Get the file format
   FILEINFO FileInfo;
   memset(&FileInfo, 0, sizeof(FILEINFO));
   FileInfo.uStructSize = sizeof(FILEINFO);
   L_INT nRet = L_FileInfo(pszFileName, &FileInfo, sizeof(FILEINFO), 0, NULL);
   if(nRet == SUCCESS)
   {
      // Check if this file format supports comment fields
      if(L_CommentsSupported(FileInfo.Format))
      {
         // Yes, read it
         L_UCHAR buf[800]="\0";
         nRet = L_ReadFileComment(pszFileName, CMNT_SZDESC, buf, 800, NULL);
         OutputDebugStringA((LPCSTR)buf);
         OutputDebugStringA("\n");
      }
      else
      {
         OutputDebugStringA("Description comment not supported\n");
      }
   }
}

Similarly, you can use L_TagsSupported and L_GeoKeysSupported to determine if the format supports tags and Geo TIFF keys respectively.

Task: Read a certain tag or comment by ID from a TIFF or EXIF file

According to the EXIF format specification, the tag with ID 0x8298 (h8298) is the EXIF tag denoting the copyright string.

The following code will print the copyright tag value found in an EXIF file:

L_VOID ReadTagById(L_TCHAR * pszFileName)
{
   // L_ReadFileTag will return an error if pszFileName does not support tags.
   // If required, use L_TagsSupported first.
   const L_INT ExifCopyrightTagId = 0x8298;
   L_UCHAR buf[800]="\0";
   L_UINT16 uType=0;
   L_UINT uCount=0;
   L_INT nRet = L_ReadFileTag(pszFileName, ExifCopyrightTagId, &uType, &uCount, buf, NULL);
   if(nRet>0)
   {
      // Tag is found, show the value
      OutputDebugStringA((LPCSTR)buf);
      OutputDebugStringA("\n");
   }
}

The following code will print the description comment value found in an EXIF file:

L_VOID ReadCommentById(L_TCHAR * pszFileName)
{
   // L_ReadFileComment will return an error if pszFileName does not support comments.
   // If required, use L_CommentsSupported first.
   L_UCHAR buf[800]="\0";
   L_INT nRet = L_ReadFileComment(pszFileName, CMNT_SZDESC, buf, 800, NULL);
   if(nRet>0)
   {
      // comment is found, show the value
      OutputDebugStringA((LPCSTR)buf);
      OutputDebugStringA("\n");
   }
}

Similarly, you can use L_ReadFileGeoKey to read an individual TIFF Geo Key by ID from a file.

Task: Write a certain tag or comment to a TIFF or EXIF file

According to the EXIF format specification, the tag with ID 0x8298 (h8298) is the EXIF tag denoting the copyright string.

The following code will update the copyright tag text in a EXIF file to "Copyright (c) My Company":

L_VOID WriteTagById(L_TCHAR * pszFileName)
{
   const L_INT ExifCopyrightTagId = 0x8298;
   // Create the tag
   L_UCHAR buf[80]="Copyright (c) My Company";
   L_INT nRet = L_SetTag(ExifCopyrightTagId, TAG_ASCII, strlen((const char*)buf)+1, (L_UCHAR*)buf);
   // Write the tag to the file
   nRet = L_WriteFileTag(pszFileName, NULL);
}

The following code will update the description comment field in an EXIF file with "My description":

L_VOID WriteCommentById(L_TCHAR * pszFileName)
{
   // Create the comment
   L_UCHAR buf[]="My description\0";
   L_INT nRet = L_SetComment(CMNT_SZDESC, (L_UCHAR*)buf, strlen((const char*)buf)+1);
   // Write it to the file
   nRet = L_WriteFileComment(pszFileName, NULL);
}

Similarly, you can use L_WriteFileGeoKey to write an individual TIFF Geo Key by ID to a file.

Task: Read all tags and comments found in a TIFF or EXIF file without loading the image

Use the following code to read all the tags found in a TIFF or EXIF file:

L_VOID ReadAllTagsWithoutLoadingImage(L_TCHAR * pszFileName)
{
   // L_ReadFileTags will return an error if pszFileName does not support tags.
   // If required, use L_TagsSupported first.
   L_UINT uTagCount=0;
   pLEADFILETAG pTags=NULL;
   L_UCHAR *pData=NULL;
   L_SIZE_T uDataSize=0;
   L_INT nRet = L_ReadFileTags(pszFileName, 0, &uTagCount, &pTags, &uDataSize, &pData, NULL);
   if(nRet == SUCCESS)
   {
      for(L_UINT i=0; i<uTagCount; i++)
      {
         L_TCHAR buf[80];
         wsprintf(buf, L_TEXT("Tag found, id=%ld\n"), pTags[i].uTag);
         OutputDebugString(buf);
      }
      L_FreeFileTags(uTagCount, pTags, uDataSize, pData);
   }
}

The code above reads all the tags as well as their data. To enumerate all the tags without reading the data, you can use L_EnumFileTags. Here is an example:

L_INT EXT_CALLBACK EnumFileTagCallback(L_UINT16  uTag,
                                       L_UINT16  uType,
                                       L_UINT32  uCount,
                                       L_VOID  * pUserData)
{
   UNREFERENCED_PARAMETER(uType);
   UNREFERENCED_PARAMETER(uCount);
   UNREFERENCED_PARAMETER(pUserData);
   L_TCHAR buf[80];
   wsprintf(buf, L_TEXT("Tag found, id=%ld\n"), uTag);
   OutputDebugString(buf);
   return SUCCESS;
}
L_VOID ReadAllTagsWithoutData(L_TCHAR * pszFileName)
{
   // L_EnumTags will return an error if pszFileName does not support tags.
   // If required, use L_TagsSupported first.
   L_INT nRet = L_EnumFileTags(pszFileName, 0, EnumFileTagCallback, NULL, NULL);
}

Use the following code to read all the comment fields found in a TIFF or EXIF file:

L_VOID ReadAllCommentsWithoutLoadingImage(L_TCHAR * pszFileName)
{
   // L_ReadFileComments will return an error if pszFileName does not support comments.
   // If required, use RasterCodecs.CommentsSupported first.
   L_UINT uCommentCount=0;
   pLEADFILECOMMENT pComments=NULL;
   L_UCHAR *pData=NULL;
   L_SIZE_T uDataSize=0;
   L_INT nRet = L_ReadFileComments(pszFileName, 0, &uCommentCount, &pComments, &uDataSize, &pData, NULL);
   if(nRet == SUCCESS)
   {
      for(L_UINT i=0; i<uCommentCount; i++)
      {
         L_TCHAR buf[80];
         wsprintf(buf, L_TEXT("Comment found, type=%ld\n"), pComments[i].uType);
         OutputDebugString(buf);
      }
      L_FreeFileComments(uCommentCount, pComments, uDataSize, pData);
   }
}

Similarly, you can use L_ReadFileGeoKeys and L_EnumFileGeoKeys to read all TIFF Geo Keys by ID from a file.

Task: Update comments or tags in a TIFF or EXIF file without loading the image

According to the EXIF format specification, the tag with ID 0x8298 (&h8298) is the EXIF tag denoting the copyright string and tag with ID 0x010E(&h010E) is the EXIF tag denoting the image title. The following code will update these tags in an EXIF file on disk with "Copyright (c) My Company" and "My Image" respectively.

L_VOID WriteTagsWithoutLoadingImage(L_TCHAR * pszFileName)
{
   // L_WriteFileTag will return an error if pszFileName does not support tags.
   // If required, use L_TagsSupported first.
   // Create the tags
   const L_INT ExifCopyrightTagId = 0x8298;
   const L_INT ExifImageTitleTagId = 0x010E;
   L_UCHAR buf1[80]="Copyright (c) My Company";
   L_INT nRet = L_SetTag(ExifCopyrightTagId, TAG_ASCII, strlen((const char*)buf1)+1, (L_UCHAR*)buf1);
   L_UCHAR buf2[80]="My Image";
   nRet = L_SetTag(ExifImageTitleTagId, TAG_ASCII, strlen((const char*)buf2)+1, (L_UCHAR*)buf2);
   // Write them to the file
   nRet = L_WriteFileTag(pszFileName, NULL);
}

Use the following code to update the copyright and description comment fields in an EXIF file without loading the image:

L_VOID WriteCommentsWithoutLoadingImage(L_TCHAR * pszFileName)
{
   // L_WriteFileComment will return an error if pszFileName does not support comments.
   // If required, use L_CommentsSupported first.
   // Create the comments
   L_UCHAR buf1[]="Copyright (c) My Company\0";
   L_INT nRet = L_SetComment(CMNT_SZCOPYRIGHT, (L_UCHAR*)buf1, strlen((const char*)buf1)+1);
   L_UCHAR buf2[]="My description\0";
   nRet = L_SetComment(CMNT_SZDESC, (L_UCHAR*)buf2, strlen((const char*)buf2)+1);
   // Write it to the file
   nRet = L_WriteFileComment(pszFileName, NULL);
}

Similarly, you can use L_WriteFileGeoKey to update the TIFF Geo Keys by ID in a file.

Task: Delete a tag by ID from a file without loading it

The fastest way to delete a tag with a known ID from a file is by using L_DeleteTag. This function has the advantage of deleting the tag directly from the disk file without requiring you to load or parse the image data.

Task: Read or write the file stamp

The stamp is a special metadata used by only EXIF, CMP, JFIF and FlashPix formats to store a thumbnail of the image. To read or write the stamp, use L_ReadFileStamp and L_WriteFileStamp.