This tutorial shows how to read and write Exif tags and comments in a Windows C/C++ API application using the LEADTOOLS SDK.
Overview | |
---|---|
Summary | This tutorial covers how to work with Exif tags and comments in a Windows C DLL application. |
Completion Time | 30 minutes |
Visual Studio Project | Download tutorial project (20 KB) |
Platform | Windows C DLL Application |
IDE | Visual Studio 2017, 2019, 2022 |
Development License | Download LEADTOOLS |
Try it in another language |
|
Get familiar with the basic steps of creating a project and loading/displaying an image by reviewing the Add References and Set a License and Load, Display, and Save Images tutorials, before working on the Read and Write Exif Tags and Comments - Windows C DLL tutorial.
Start with a copy of the project created in the Load, Display, and Save Images tutorial. If the project is not available, create it by following the steps in that tutorial.
Open the pre-compiled header file (either pch.h
or stdafx.h
, depending on the version of Visual Studio used) and ensure the below lines are added.
#define LTV23_CONFIG
#include "C:\LEADTOOLS23\Include\L_Bitmap.h" // use the actual path where LEADTOOLS is installed
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\Ltkrn_x.lib")
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\Ltfil_x.lib") // file loading and saving
#pragma comment (lib, "C:\\LEADTOOLS23\\Lib\\CDLL\\x64\\Ltdis_x.lib") // image display
Note
For a complete list of DLLs that are required for specific application features, refer to Files to be Included with your Application - C API
The License unlocks the features needed for the project. It must be set before any toolkit functionality is called. For details, including tutorials for different platforms, refer to Setting a Runtime License.
There are two types of runtime licenses:
Note
Adding LEADTOOLS references and setting a license are covered in more detail in the Add References and Set a License tutorial.
With the project created, the references added, the license set, and the load image code added, coding can begin.
In the Solution Explorer, double-click the resources file (.rc).
Add a new Exif &Comment menu item to the File drop-down menu, between the Open and Save items. Leave the new menu item's ID as ID_FILE_EXIFCOMMENT
.
Open the project's CPP file and navigate to the WndProc
function. Under the switch (wmId)
statement that is below the WM_COMMAND
case, add a new case and the code below.
switch (wmId)
{
case ID_FILE_EXIFCOMMENT:
ReadAndWriteExifComments(hWnd);
break;
// Keep rest of the code as is
Add the ReadAndWriteExifComments function below to flip the loaded image, write a comment to the file stating that the image has been flipped, and then read the user comment created.
void ReadAndWriteExifComments(HWND hWnd)
{
L_TCHAR szSrcFile[260] = TEXT(""), szDestFile[260] = TEXT("");
if (SUCCESS != GetBitmapLoadingName(hWnd, szSrcFile, ARRAYSIZE(szSrcFile)))
return;
if(LEADBmp.Flags.Allocated)
L_FreeBitmap(&LEADBmp);
L_LoadBitmap(szSrcFile, &LEADBmp, sizeof(LEADBmp), 24, ORDER_BGR, NULL, NULL);
InvalidateRect(hWnd, NULL, TRUE);
MessageBox(hWnd, TEXT("Image loaded and will be flipped.\nSelect output file"), TEXT("LEADTOOLS Demo"), MB_ICONINFORMATION);
// Flip the image.
L_FlipBitmap(&LEADBmp);
InvalidateRect(hWnd, NULL, TRUE);
// Specify output file
if (SUCCESS != GetBitmapSavingName(hWnd, szDestFile, ARRAYSIZE(szDestFile)))
return;
// Load the current comments from the file
for (int i = 0; i < CMNT_LAST; i++)
{
// Clear the comment
L_SetComment(i, NULL, 0);
// Use the return value to get the length of a comment in the file
L_INT CommentLength = L_ReadFileComment(szSrcFile, i, NULL, 0, NULL);
if (CommentLength > 0)
{
L_UCHAR* pCommentToGet = new L_UCHAR[CommentLength](); // parentheses initialize array to zeros
// Get the comment from the file
L_ReadFileComment(szSrcFile, i, pCommentToGet, CommentLength, NULL);
// Set the comment to be saved
L_SetComment(i, pCommentToGet, CommentLength);
delete[] pCommentToGet;
}
}
// Create the CMNT_USERCOMMENT comment as a byte array
// When writing a user comment, the first 8 bytes must contain the "ASCII" word.
L_SetComment(CMNT_USERCOMMENT, NULL, 0);
// IMPORTANT: initializing CommentToSet array causes remainder of array to contain null characters, which is needed for rest of code to work
L_UCHAR CommentToSet[80] = "ASCII";
const L_CHAR* pUserComment = "The image has been flipped!";
strcpy_s((char *)CommentToSet + 8, sizeof CommentToSet - 8, pUserComment);
// Set the CMNT_USERCOMMENT comment
L_SetComment(CMNT_USERCOMMENT, CommentToSet, (L_UINT)strlen(pUserComment) + 9);
L_SaveBitmap(szDestFile, &LEADBmp, FILE_EXIF, 24, 0, NULL);
L_UCHAR CommentCheck[80] = "";
L_ReadFileComment(szDestFile, CMNT_USERCOMMENT, CommentCheck, sizeof CommentCheck, NULL);
char szMessageA[256] = "Comment read back from file:\n";
strcat_s(szMessageA, sizeof szMessageA, (const char *)CommentCheck + 8);
// The returned string is in ASCII, so the "MessageBoxA" function is used to display it
MessageBoxA(hWnd, szMessageA, "LEADTOOLS Demo", MB_OK);
}
The code for the GetBitmapLoadingName()
and GetBitmapSavingName()
functions, called inside the code above, is listed in the Load, Display, and Save Images tutorial.
Using the Solution Explorer, navigate back to the resources file (.rc).
Add a new EXIF &Tag menu item to the File drop-down menu, between the Open and Save items. Leave the new menu item's ID as ID_FILE_EXIFTAG
.
Go to the WndProc
function and under the switch (wmId)
statement that is below the WM_COMMAND
case, add a new case and the code below.
switch (wmId)
{
case ID_FILE_EXIFTAG:
ReadAndWriteExifTags(hWnd);
break;
// Keep rest of the code as is
Add the code of the ReadAndWriteExifTags
function, which adds a custom text tag to the EXIF file then reads all the tags back from the file.
void ReadAndWriteExifTags(HWND hWnd)
{
L_TCHAR szExifFile[260] = TEXT("");
if (SUCCESS != GetBitmapLoadingName(hWnd, szExifFile, ARRAYSIZE(szExifFile)))
return;
FILEINFO info = { 0 }; // Important: Must initialize to zero
info.uStructSize = sizeof info;
L_FileInfo(szExifFile, &info, sizeof info, 0, NULL);
if (!L_TagsSupported(info.Format))
{
MessageBox(hWnd, TEXT("File Format Does Not Support Tags"), TEXT("LEADTOOLS Demo"), MB_ICONERROR);
return;
}
// Add a private (custom) "Phone Number" tag
L_UINT16 PhoneNumber = 0x8001;
char TagData[] = "+1-800-637-4699";
// Set the tag data to be saved
L_SetTag(PhoneNumber, TAG_ASCII, sizeof TagData, TagData);
// Save the the tag into the file
L_WriteFileTag(szExifFile, NULL);
// Clear the tag from memory
L_SetTag(0, 0, 0, NULL);
// Read the tags from the file and display them in the debugger window
L_UINT uTagCount = 0;
pLEADFILETAG pTags = NULL, pCurrentTag;
L_SIZE_T uTagDataSize = 0;
L_UCHAR* pTagsData = NULL;
L_ReadFileTags(szExifFile, 0, &uTagCount, &pTags, &uTagDataSize, &pTagsData, NULL);
if (uTagCount > 0)
{
// Show the tags
OutputDebugString(TEXT("Tags\n"));
for (L_UINT n = 0; n < uTagCount; n++)
{
pCurrentTag = &pTags[n];
// If this tag is of type ASCII, get its data
L_CHAR* pszAscii = NULL;
if (pCurrentTag->uType == TAG_ASCII)
{
pszAscii = new L_CHAR[pCurrentTag->uDataSize + 1]();
memcpy(pszAscii, pTagsData + pCurrentTag->uDataOffset, pCurrentTag->uDataSize);
}
else
{
pszAscii = NULL;
}
CHAR szMessage[1024];
wsprintfA(szMessage, "Id: 0x%X, data length: %u, data: %s\n", pCurrentTag->uTag, pCurrentTag->uDataSize, pszAscii != NULL ? pszAscii : "Binary data");
if (pszAscii)
delete[] pszAscii;
OutputDebugStringA(szMessage);
}
L_FreeFileTags(uTagCount, pTags, uTagDataSize, pTagsData);
}
}
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps are followed correctly, the application runs and gives the user the ability to execute the following commands:
This tutorial showed how to use the L_SetComment
, L_ReadFileComment
, L_ReadFileTags
, L_SetTag
and L_WriteFileTag
functions with EXIF files.