質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.69%

GDI+ でのjpgタグ操作

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 1,011

BeatStar

score 1689

C/C++ ( いわゆる BetterC ) でやっています。

基本的には MinGW ( g++ ) でやっていますが、GDI+ を使うため VC++ で DLL作成しています。

Jpgタグで "キーワードタグ" ってありますよね。

Windows 付属の Explorer で jpgファイルを選択し、

「プロパティ(R)」 → 「詳細」タブ → 「タグ」

で追加・削除できるやつです。

これの編集ができるソフトを作りたいと思い、

「C言語 jpgタグ 編集」等で検索したところ、
GDI+ というライブラリでできるようです。

なので、GDI+ についていたヘルプファイル のサンプルコードを流用して組んでみました。

しかし、挙動が変なのです。

たとえば、

"友人;中学校;卒業;涙;abc;太郎;次郎;三郎" 

というタグが埋め込まれているとします。

読み込んだとき、

なぜか、

"友人;中学校;卒業;涙;ab"

と途中までしか表示されません。

長さが足りないのかと思い、
キーワードタグを短くすると、
同じように途中までしか表示されません。( むしろ、表示できる部分が狭くなっている。 )

実際のキーワードタグ全体の長さ - 55

等のようになっているのかもしれません。

ですが、ソースコードのどこを書き換えるべきなのか。。。

[ DLL ( VC++作成 ) 側 ]:

/* ライブラリ内の関数をオープンにするためのもの */
#define FUNCTION_DEFINITION extern "C" __declspec(dllexport)


/* GDI+ をリンクする */
#pragma comment (lib, "gdiplus.lib")

/* GDI+ で必要最低限のもの */
// 定義済みとする

void PropertyTypeFromWORD(WORD index, WCHAR* string);
int  GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

#define MAX_TAG 177


/* コール時に受け取る可能性のある種類 */
#define ID_KEYWORD_TAG        (0)
#define ID_COMMENT_TAG        (1)
#define ID_USER_COMMNET_TAG   (1)

/* 変換用 */
#define ID_TO_GIF             (0)
#define ID_TO_JPG             (1)
#define ID_TO_PNG             (2)
#define ID_TO_BMP             (3)

/* 現在 取得および設定可能なタグ */
#define KEYWORD_TAG    0x9c9e
#define COMMENT_TAG    0x9c9c

#define GDIPLUS_INITIALIZE      GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL)



// GD+のヘルプファイルから。
// Helper function
void PropertyTypeFromWORD(WORD index, WCHAR* string)
{
   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

   wcscpy(string, propertyTypes[index]);
}

// GD+のヘルプファイルから。
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}



// C言語のchar型配列を WCHAR型配列 に変換
void CStringToWCHAR( char *cStr, WCHAR *wStr )
{
      const int len = (int)strlen( cStr );

      setlocale( LC_CTYPE, "" );

      mbstowcs( wStr, cStr, len + 1 );
}


// WCHAR型配列 を C言語のchar型配列 に変換
void WcharToCString( WCHAR *wStr, char *cStr )
{
      const int len = (int)wcslen( wStr );
      setlocale( LC_CTYPE, "" );

      wcstombs( cStr, wStr, len + 1 );
}

// GD+のヘルプファイルから。
bool WriteTag( WCHAR *JpgFilePath, int TagID, char *str, WCHAR *NewjpgPathName )
{
   // Initialize GDI+.
   GDIPLUS_INITIALIZE;

   bool bStatus = false;

   Status stat;
   CLSID  clsid;
   WCHAR   wPropertyValue[6000];

   Bitmap* bitmap = new Bitmap(JpgFilePath);
   PropertyItem* propertyItem = new PropertyItem;

   // Get the CLSID of the JPEG encoder.
   GetEncoderClsid(L"image/jpeg", &clsid);



   if( TagID == KEYWORD_TAG )
   {
              CStringToWCHAR( str, wPropertyValue );
              propertyItem->id = KEYWORD_TAG;
              propertyItem->length = MAX_TAG*2;  // string length including NULL terminator
              propertyItem->type = PropertyTagTypeByte; 
              propertyItem->value = wPropertyValue;
   }
   bitmap->SetPropertyItem(propertyItem);

   stat = bitmap->Save(NewjpgPathName, &clsid, NULL);
   if(stat == Ok) bStatus = true;

   delete propertyItem;
   delete bitmap;

   GDIPLUS_UNINITIALIZE;
 return bStatus;
}

// GDI+ のヘルプファイルから。
bool ReadTag( WCHAR *JpgFilePath, int TagID, char *result )
{
     // Initialize GDI+.
     GDIPLUS_INITIALIZE;

   UINT  size = 0;
   UINT  count = 0;
   WCHAR strPropertyType[50] = L"";

   char strID[9];
   bool flag = false;

   Bitmap* bitmap = new Bitmap( JpgFilePath );

   bitmap->GetPropertySize(&size, &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);

      sprintf( strID, "0x%x", pPropBuffer[j].id );

      if( strcmp( strID, "0x9c9e" ) == 0 ) // "キーワード" タグ
      {
          WcharToCString( (WCHAR*)pPropBuffer[j].value, result );
          flag = true;
          break;
      }
   }

   free(pPropBuffer);
   delete bitmap;

   GDIPLUS_UNINITIALIZE;
return flag;
}

FUNCTION_DEFINITION
bool ReadTagEx( char *JpgFilePath, int iKind, char *result )
{

      WCHAR wJpgFilePath[MAX_PATH+1];
      int   TagID;

      CStringToWCHAR( JpgFilePath, wJpgFilePath );

      if( iKind == ID_KEYWORD_TAG )
      {
                TagID = KEYWORD_TAG;
      }
      else if( iKind == ID_COMMENT_TAG || iKind == ID_USER_COMMNET_TAG )
      {
                TagID = COMMENT_TAG;
      }
return ReadTag( wJpgFilePath, TagID, result );
}



FUNCTION_DEFINITION
bool WriteTagEx( char *JpgFilePath, int iKind, char *str, char *NewJpgFilePath )
{
      WCHAR wJpgFilePath[MAX_PATH+1];
      WCHAR wJpgNewFilePath[MAX_PATH+1];
      int TagID;

      CStringToWCHAR( JpgFilePath, wJpgFilePath );
      CStringToWCHAR( NewJpgFilePath, wJpgNewFilePath );

      if( iKind == ID_KEYWORD_TAG )
      {
                TagID = KEYWORD_TAG;
      }
      else if( iKind == ID_COMMENT_TAG || iKind == ID_USER_COMMNET_TAG )
      {
                TagID = COMMENT_TAG;
      }
return WriteTag( wJpgFilePath, TagID, str, wJpgNewFilePath );
}

キーワードタグ用のIDは サンプルコードで ID がある間ループさせ,
それっぽい ID をチェックしたのですが、
ここからどうすればいいか。。。

[環境等]
言語: C/C++ ( いわゆる Better C )
コンパイラ: VC++ & MinGW

宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.69%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる