Lecture et écriture de métadonnées

Certains fichiers image contiennent des métadonnées que vous pouvez lire pour déterminer les caractéristiques de l’image. Par exemple, une photo numérique peut contenir des métadonnées que vous pouvez lire pour déterminer la fabrique et le modèle de l’appareil photo utilisé pour capturer l’image. Avec Windows GDI+, vous pouvez lire les métadonnées existantes et écrire de nouvelles métadonnées dans des fichiers image.

GDI+ offre un moyen uniforme de stocker et de récupérer des métadonnées à partir de fichiers image dans différents formats. Dans GDI+, un élément de métadonnées est appelé élément de propriété. Vous pouvez stocker et récupérer des métadonnées en appelant les méthodes SetPropertyItem et GetPropertyItem de la classe Image , et vous n’avez pas à vous soucier des détails de la façon dont un format de fichier particulier stocke ces métadonnées.

GDI+ prend actuellement en charge les métadonnées pour les formats de fichiers TIFF, JPEG, Exif et PNG. Le format Exif, qui spécifie comment stocker des images capturées par des appareils photo fixes numériques, s’appuie sur les formats TIFF et JPEG. Exif utilise le format TIFF pour les données de pixels non compressées et le format JPEG pour les données de pixels compressées.

GDI+ définit un ensemble de balises de propriété qui identifient les éléments de propriété. Certaines balises sont à usage général ; autrement dit, ils sont pris en charge par tous les formats de fichier mentionnés dans le paragraphe précédent. Les autres balises sont à usage spécial et s’appliquent uniquement à certains formats. Si vous essayez d’enregistrer un élément de propriété dans un fichier qui ne prend pas en charge cet élément de propriété, GDI+ ignore la demande. Plus précisément, la méthode Image::SetPropertyItem retourne PropertyNotSupported.

Vous pouvez déterminer les éléments de propriété stockés dans un fichier image en appelant Image::GetPropertyIdList. Si vous essayez de récupérer un élément de propriété qui n’est pas dans le fichier, GDI+ ignore la demande. Plus précisément, la méthode Image::GetPropertyItem retourne PropertyNotFound.

Lecture des métadonnées à partir d’un fichier

L’application console suivante appelle la méthode GetPropertySize d’un objet Image pour déterminer le nombre d’éléments de métadonnées dans le fichier FakePhoto.jpg.

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
   // Initialize <tla rid="tla_gdiplus"/>.
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
   UINT    size = 0;
   UINT    count = 0;
   Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
   bitmap->GetPropertySize(&size, &count);
   printf("There are %d pieces of metadata in the file.\n", count);
   printf("The total size of the metadata is %d bytes.\n", size);
 
   delete bitmap;
   GdiplusShutdown(gdiplusToken);
   return 0;
}

Le code précédent, ainsi qu’un fichier particulier, FakePhoto.jpg, ont produit la sortie suivante :

There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.

GDI+ stocke un élément de métadonnées individuel dans un objet PropertyItem . Vous pouvez appeler la méthode GetAllPropertyItems de la classe Image pour récupérer toutes les métadonnées d’un fichier. La méthode GetAllPropertyItems retourne un tableau d’objets PropertyItem . Avant d’appeler GetAllPropertyItems, vous devez allouer une mémoire tampon suffisamment grande pour recevoir ce tableau. Vous pouvez appeler la méthode GetPropertySize de la classe Image pour obtenir la taille (en octets) de la mémoire tampon requise.

Un objet PropertyItem a les quatre membres publics suivants :

Description
id Balise qui identifie l’élément de métadonnées. Les valeurs qui peuvent être attribuées à id (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime, etc.) sont définies dans Gdiplusimaging.h.
length Longueur, en octets, du tableau de valeurs pointées par le membre de données de valeur . Notez que si le membre de données de type est défini sur PropertyTagTypeASCII, le membre de données de longueur correspond à la longueur d’une chaîne de caractères terminée par null, y compris la terminaison NULL.
type Type de données des valeurs du tableau pointé vers le membre de données value. Les constantes (PropertyTagTypeByte, PropertyTagTypeASCII, etc.) qui représentent différents types de données sont décrites dans Constantes de type de balise de propriété image.
value Pointeur vers un tableau de valeurs.

 

L’application console suivante lit et affiche les sept éléments de métadonnées dans le fichier FakePhoto.jpg. La fonction main s’appuie sur la fonction d’assistance PropertyTypeFromWORD, qui s’affiche après la fonction main.

#include <windows.h>
#include <gdiplus.h>
#include <strsafe.h>
using namespace Gdiplus;

INT main()
{
   // Initialize GDI+
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

   UINT  size = 0;
   UINT  count = 0;

   #define MAX_PROPTYPE_SIZE 30
   WCHAR strPropertyType[MAX_PROPTYPE_SIZE] = L"";

   Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");

   bitmap->GetPropertySize(&size, &count);
   printf("There are %d pieces of metadata in the file.\n\n", count);

   // GetAllPropertyItems returns an array of PropertyItem objects.
   // Allocate a buffer large enough to receive that array.
   PropertyItem* pPropBuffer =(PropertyItem*)malloc(size);

   // Get the array of PropertyItem objects.
   bitmap->GetAllPropertyItems(size, count, pPropBuffer);

   // For each PropertyItem in the array, display the id, type, and length.
   for(UINT j = 0; j < count; ++j)
   {
      // Convert the property type from a WORD to a string.
      PropertyTypeFromWORD(
         pPropBuffer[j].type, strPropertyType, MAX_PROPTYPE_SIZE);

      printf("Property Item %d\n", j);
      printf("  id: 0x%x\n", pPropBuffer[j].id);
      wprintf(L"  type: %s\n", strPropertyType);
      printf("  length: %d bytes\n\n", pPropBuffer[j].length);
   }

   free(pPropBuffer);
   delete bitmap;
   GdiplusShutdown(gdiplusToken);
   return 0;
} // main

// Helper function
HRESULT PropertyTypeFromWORD(WORD index, WCHAR* string, UINT maxChars)
{
   HRESULT hr = E_FAIL;

   WCHAR* propertyTypes[] = {
      L"Nothing",                   // 0
      L"PropertyTagTypeByte",       // 1
      L"PropertyTagTypeASCII",      // 2
      L"PropertyTagTypeShort",      // 3
      L"PropertyTagTypeLong",       // 4
      L"PropertyTagTypeRational",   // 5
      L"Nothing",                   // 6
      L"PropertyTagTypeUndefined",  // 7
      L"Nothing",                   // 8
      L"PropertyTagTypeSLONG",      // 9
      L"PropertyTagTypeSRational"}; // 10

   hr = StringCchCopyW(string, maxChars, propertyTypes[index]);
   return hr;
}

L’application console précédente produit la sortie suivante :

Property Item 0
  id: 0x320
  type: PropertyTagTypeASCII
  length: 16 bytes
Property Item 1
  id: 0x10f
  type: PropertyTagTypeASCII
  length: 17 bytes
Property Item 2
  id: 0x110
  type: PropertyTagTypeASCII
  length: 7 bytes
Property Item 3
  id: 0x9003
  type: PropertyTagTypeASCII
  length: 20 bytes
Property Item 4
  id: 0x829a
  type: PropertyTagTypeRational
  length: 8 bytes
Property Item 5
  id: 0x5090
  type: PropertyTagTypeShort
  length: 128 bytes
Property Item 6
  id: 0x5091
  type: PropertyTagTypeShort
  length: 128 bytes

La sortie précédente affiche un numéro d’ID hexadécimal pour chaque élément de propriété. Vous pouvez rechercher ces numéros d’ID dans Constantes d’étiquette de propriété d’image et découvrir qu’ils représentent les balises de propriété suivantes.

Valeur hexadécimale Balise de propriété
0x0320 0x010f
  0x0110
  0x9003
  0x829a
  0x5090
  0x5091
PropertyTagImageTitle PropertyTagEquipMake
  PropertyTagEquipModel
  PropertyTagExifDTOriginal
  PropertyTagExifExposureTime
  PropertyTagLuminanceTable
  PropertyTagChrominanceTable

 

Le deuxième élément de propriété (index 1) de la liste a l’id PropertyTagEquipMake et le type PropertyTagTypeASCII. L’exemple suivant, qui est une continuation de l’application console précédente, affiche la valeur de cet élément de propriété :

printf("The equipment make is %s.\n", pPropBuffer[1].value);

La ligne de code précédente produit la sortie suivante :

The equipment make is Northwind Traders.

Le cinquième élément de propriété (index 4) de la liste a l’id PropertyTagExifExposureTime et le type PropertyTagTypeRational. Ce type de données (PropertyTagTypeRational) est une paire de longs. L’exemple suivant, qui est une continuation de l’application console précédente, affiche ces deux valeurs LONG sous forme de fraction. Cette fraction, 1/125, correspond au temps d’exposition mesuré en secondes.

long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);

Le code ci-dessus génère la sortie suivante :

The exposure time is 1/125.

Écriture de métadonnées dans un fichier

Pour écrire un élément de métadonnées dans un objet Image , initialisez un objet PropertyItem , puis transmettez l’adresse de cet objet PropertyItem à la méthode SetPropertyItem de l’objet Image .

L’application console suivante écrit un élément (le titre de l’image) des métadonnées dans un objet Image , puis enregistre l’image dans le fichier disque FakePhoto2.jpg. La fonction main s’appuie sur la fonction d’assistance GetEncoderClsid, qui est présentée dans la rubrique Récupération de l’identificateur de classe pour un encodeur.

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
   // Initialize <tla rid="tla_gdiplus"/>.
   GdiplusStartupInput gdiplusStartupInput;
   ULONG_PTR gdiplusToken;
   GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
   Status stat;
   CLSID  clsid;
   char   propertyValue[] = "Fake Photograph";
   Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
   PropertyItem* propertyItem = new PropertyItem;
   // Get the CLSID of the JPEG encoder.
   GetEncoderClsid(L"image/jpeg", &clsid);
   propertyItem->id = PropertyTagImageTitle;
   propertyItem->length = 16;  // string length including NULL terminator
   propertyItem->type = PropertyTagTypeASCII; 
   propertyItem->value = propertyValue;
   bitmap->SetPropertyItem(propertyItem);
   stat = bitmap->Save(L"FakePhoto2.jpg", &clsid, NULL);
   if(stat == Ok)
      printf("FakePhoto2.jpg saved successfully.\n");
   
   delete propertyItem;
   delete bitmap;
   GdiplusShutdown(gdiplusToken);
   return 0;
}