Lectura y escritura de metadatos
Algunos archivos de imagen contienen metadatos que se pueden leer para determinar las características de la imagen. Por ejemplo, una fotografía digital podría contener metadatos que se pueden leer para determinar la marca y el modelo de la cámara empleada para capturar la imagen. Con GDI+, puede leer los metadatos existentes y también puede escribir nuevos metadatos en archivos de imagen.
GDI+ proporciona una manera uniforme de almacenar y recuperar metadatos de archivos de imagen en varios formatos. En GDI+, un fragmento de metadatos se denomina elemento de propiedad. Puede almacenar y recuperar metadatos llamando a los métodos SetPropertyItem y GetPropertyItem de la clase Image , y no es necesario preocuparse por los detalles de cómo un formato de archivo determinado almacena esos metadatos.
GDI+ admite actualmente metadatos para los formatos de archivo TIFF, JPEG, Exif y PNG. El formato Exif, que especifica cómo almacenar imágenes capturadas por cámaras fijas digitales, se basa en los formatos TIFF y JPEG. Exif usa el formato TIFF para los datos de píxeles sin comprimir y el formato JPEG para los datos de píxeles comprimidos.
GDI+ define un conjunto de etiquetas de propiedad que identifican elementos de propiedad. Ciertas etiquetas son de uso general; es decir, son compatibles con todos los formatos de archivo mencionados en el párrafo anterior. Otras etiquetas son de uso especial y solo se aplican a determinados formatos. Si intenta guardar un elemento de propiedad en un archivo que no admite ese elemento de propiedad, GDI+ omite la solicitud. Más concretamente, el método Image::SetPropertyItem devuelve PropertyNotSupported.
Puede determinar los elementos de propiedad almacenados en un archivo de imagen llamando a Image::GetPropertyIdList. Si intenta recuperar un elemento de propiedad que no está en el archivo, GDI+ omite la solicitud. Más concretamente, el método Image::GetPropertyItem devuelve PropertyNotFound.
Lectura de metadatos de un archivo
La siguiente aplicación de consola llama al método GetPropertySize de un objeto Image para determinar cuántos fragmentos de metadatos hay en el archivo 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;
}
El código anterior, junto con un archivo determinado, FakePhoto.jpg, generó la siguiente salida:
There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.
GDI+ almacena un fragmento individual de metadatos en un objeto PropertyItem . Puede llamar al método GetAllPropertyItems de la clase Image para recuperar todos los metadatos de un archivo. El método GetAllPropertyItems devuelve una matriz de objetos PropertyItem . Antes de llamar a GetAllPropertyItems, debe asignar un búfer lo suficientemente grande como para recibir esa matriz. Puede llamar al método GetPropertySize de la clase Image para obtener el tamaño (en bytes) del búfer necesario.
Un objeto PropertyItem tiene los cuatro miembros públicos siguientes:
Descripción | |
---|---|
id | Etiqueta que identifica el elemento de metadatos. Los valores que se pueden asignar a id (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime y like) se definen en Gdiplusimaging.h. |
length | Longitud, en bytes, de la matriz de valores a la que apunta el miembro de datos value. Tenga en cuenta que si el miembro de datos de tipo se establece en PropertyTagTypeASCII, el miembro de datos length es la longitud de una cadena de caracteres terminada en NULL, incluido el terminador NULL. |
type | Tipo de datos de los valores de la matriz a los que apunta el miembro de datos value. Constantes (PropertyTagTypeByte, PropertyTagTypeASCII y similares) que representan varios tipos de datos se describen en Constantes de tipo de etiqueta de propiedad image. |
value | Puntero a una matriz de valores. |
La siguiente aplicación de consola lee y muestra los siete fragmentos de metadatos del archivo FakePhoto.jpg. La función main se basa en la función auxiliar PropertyTypeFromWORD, que se muestra después de la función principal.
#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;
}
La aplicación de consola anterior genera la siguiente salida:
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 salida anterior muestra un número de identificador hexadecimal para cada elemento de propiedad. Puede buscar esos números de identificador en Constantes de etiqueta de propiedad de imagen y averiguar que representan las siguientes etiquetas de propiedad.
Valor hexadecimal | Etiqueta de propiedad |
---|---|
0x0320 0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
PropertyTagImageTitle PropertyTagEquipMake PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExifExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
El segundo elemento de propiedad (índice 1) de la lista tiene el identificador PropertyTagEquipMake y el tipo PropertyTagTypeASCII. En el ejemplo siguiente, que es una continuación de la aplicación de consola anterior, se muestra el valor de ese elemento de propiedad:
printf("The equipment make is %s.\n", pPropBuffer[1].value);
La línea de código anterior genera la siguiente salida:
The equipment make is Northwind Traders.
El quinto elemento de propiedad (índice 4) de la lista tiene el identificador PropertyTagExifExposureTime y el tipo PropertyTagTypeRational. Ese tipo de datos (PropertyTagTypeRational) es un par de LONGs. En el ejemplo siguiente, que es una continuación de la aplicación de consola anterior, se muestran esos dos valores LONG como una fracción. Esa fracción, 1/125, es el tiempo de exposición medido en segundos.
long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);
Este código anterior genera el siguiente resultado:
The exposure time is 1/125.
Escribir metadatos en un archivo
Para escribir un elemento de metadatos en un objeto Image , inicialice un objeto PropertyItem y, a continuación, pase la dirección de ese objeto PropertyItem al método SetPropertyItem del objeto Image .
La siguiente aplicación de consola escribe un elemento (el título de imagen) de los metadatos en un objeto Image y, a continuación, guarda la imagen en el archivo de disco FakePhoto2.jpg. La función principal se basa en la función auxiliar GetEncoderClsid, que se muestra en el tema Recuperación del identificador de clase para un codificador.
#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;
}