讀取和寫入中繼資料

某些影像檔案包含您可以讀取以判斷影像功能的中繼資料。 例如,數位相片可能包含中繼資料,您可以讀取以判斷用來擷取影像之相機的製作和模型。 使用 Windows GDI+,您可以讀取現有的中繼資料,也可以將新的中繼資料寫入影像檔。

GDI+ 提供統一的方式,以各種格式儲存和擷取影像檔案的中繼資料。 在 GDI+中,中繼資料片段稱為 屬性專案。 您可以呼叫Image類別的SetPropertyItemGetPropertyItem方法來儲存和擷取中繼資料,而且您不需要擔心特定檔案格式如何儲存該中繼資料的詳細資料。

GDI+ 目前支援 TIFF、JPEG、Exif 和 PNG 檔案格式的中繼資料。 Exif 格式會指定如何儲存數位相機所擷取的影像,是以 TIFF 和 JPEG 格式為基礎。 Exif 會針對未壓縮的圖元資料使用 TIFF 格式,以及壓縮圖元資料的 JPEG 格式。

GDI+ 會定義一組可識別屬性專案的屬性標記。 某些標記是一般用途;也就是說,上述段落中所述的所有檔案格式都支援它們。 其他標籤是特殊用途,僅適用于特定格式。 如果您嘗試將屬性專案儲存到不支援該屬性專案的檔案,GDI+ 會忽略要求。 更具體來說, Image::SetPropertyItem 方法會傳回 PropertyNotSupported。

您可以呼叫 Image::GetPropertyIdList來判斷儲存在圖像檔中的屬性專案。 如果您嘗試擷取不在檔案中的屬性專案,GDI+ 會忽略要求。 更具體來說, Image::GetPropertyItem 方法會傳回 PropertyNotFound。

從檔案讀取中繼資料

下列主控台應用程式會呼叫Image物件的GetPropertySize方法,以判斷檔案中有多少個中繼資料片段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;
}

上述程式碼以及特定檔案FakePhoto.jpg會產生下列輸出:

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

GDI+ 會將個別的中繼資料片段儲存在 PropertyItem 物件中。 您可以呼叫Image類別的GetAllPropertyItems方法,以從檔案擷取所有中繼資料。 GetAllPropertyItems方法會傳回PropertyItem物件的陣列。 呼叫 GetAllPropertyItems之前,您必須配置足以接收該陣列的緩衝區。 您可以呼叫Image類別的GetPropertySize方法來取得所需緩衝區) 位元組大小 (。

PropertyItem物件具有下列四個公用成員:

描述
id 識別中繼資料專案的標記。 可以指派給 id (PropertyTagImageTitle、PropertyTagEquipMake、PropertyTagExifExposureTime 等) 的值定義在 Gdiplusimaging.h 中。
length (長度) 資料成員 所指向值陣列的長度,以位元組為單位。 請注意,如果 類型 資料成員設定為 PropertyTagTypeASCII,則長度資料成員是 Null 終止字元字串的 長度 ,包括 Null 結束字元。
type 值資料成員所指向之陣列中值的資料類型。 代表各種資料類型的常數 (PropertyTagTypeByte、PropertyTagTypeASCII 等) 都會在 Image 屬性標記類型常數中說明。
value 值的陣列指標。

 

下列主控台應用程式會讀取並顯示檔案中FakePhoto.jpg的七個中繼資料片段。 main 函式依賴 Helper 函式 PropertyTypeFromWORD,如 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;
}

上述主控台應用程式會產生下列輸出:

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

上述輸出會顯示每個屬性專案的十六進位識別碼。 您可以在 Image Property Tag 常數 中查閱這些識別碼,並找出它們代表下列屬性標記。

十六進位值 屬性標籤
0x0320 0x010f
  0x0110
  0x9003
  0x829a
  0x5090
  0x5091
PropertyTagImageTitle PropertyTagEquipMake
  PropertyTagEquipModel
  PropertyTagExifDTOriginal
  PropertyTagExifExposureTime
  PropertyTagLuminanceTable
  PropertyTagChrominanceTable

 

清單中的第二個 (索引 1) 屬性專案具有 id PropertyTagEquipMake,並 輸入 PropertyTagTypeASCII。 下列範例是先前主控台應用程式的接續,會顯示該屬性專案的值:

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

上述程式程式碼會產生下列輸出:

The equipment make is Northwind Traders.

清單中的第五個 (索引 4) 屬性項具有 id PropertyTagExifExposureTime,並 輸入 PropertyTagTypeRational。 該資料類型 (PropertyTagTypeRational) 是一對 LONG。 下列範例是先前主控台應用程式的接續,會將這兩個 LONG 值顯示為分數。 該分數 1/125 是以秒為單位測量的曝光時間。

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

前一個程式碼會產生下列輸出:

The exposure time is 1/125.

將中繼資料寫入檔案

若要將中繼資料的專案寫入Image物件,請初始化PropertyItem物件,然後將該PropertyItem物件的位址傳遞至Image物件的SetPropertyItem方法。

下列主控台應用程式會將一個專案 (中繼資料的影像標題) 寫入 Image 物件,然後將映射儲存在磁片檔案FakePhoto2.jpg中。 main 函式依賴 Helper 函式 GetEncoderClsid,如擷 取編碼器的類別識別碼主題所示。

#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;
}