具体的な手順を示しますが、移植先はVCLかFMXのどちらでも可
前記事で紹介した
ここのexif.cとexif.hを移植します。(ま、移植という程ではないですが…..。)同化(assimilate)というべきかな、筆者が付け加えた部分はほぼゼロです。作者と連絡が付いて、許諾が得られれば変更後のソースも提示させてもらいます。
まずは、exif.cの拡張子を.cから.cppに変更します。ファイルエクスプローラーから拡張子を書き換えます、警告が出ますけど無視。exif.cppとexif.hを新規のプロジェクトに追加します。追加した状態(fmxの場合)が下図、

IDEのプロジェクトエクスプローラーでは、

のように見えます。さてと、コンパイルしてみましょう。エラーが出たら、逐一修正すれば良いです。(たぶん)コンパイルオンリーは、Shift-F9ですね。

さっそくエラーが出ました。18個もありますが、同様なエラーが複数回カウントされているだけで、特に大きくソースを変更しなければいけないものではありません。さて、最初のは、

コンパイラ様曰く:
[bcc64 エラー] exif.cpp(271): assigning to 'IfdTable *' (aka '_ifdTable *') from incompatible type 'void *'
[bcc64 エラー] exif.cpp(286): assigning to 'IfdTable *' (aka '_ifdTable *') from incompatible type 'void *'
[bcc64 エラー] exif.cpp(294): assigning to 'IfdTable *' (aka '_ifdTable *') from incompatible type 'void *'
[bcc64 エラー] exif.cpp(319): assigning to 'IfdTable *' (aka '_ifdTable *') from incompatible type 'void *'
[bcc64 エラー] exif.cpp(334): assigning to 'IfdTable *' (aka '_ifdTable *') from incompatible type 'void *'
Cではvoidへのポインターをキャスト無しで任意のポインターへ代入できたんですが、C++ではアウトということです。よって、
ifd_0th = parseIFD(fp, App1Header.tiff.Ifd0thOffset, IFD_0TH);
を
ifd_0th = (IfdTable *)parseIFD(fp, App1Header.tiff.Ifd0thOffset, IFD_0TH);
と明示的にキャストすれば通るはずですね。再度Shift-F9します。

同様です。parseIFDが返すものを(IfdTable *)へキャストします。これをエラーが出たところで繰り返します。面倒ならば、IDEの置換機能を使って、

から”すべて置換”しても良いです。

で”はい”を選択ですね。箇所がたかだか数カ所なので、逐一確認するのが吉です。大した手間ではありませんから。置換後Shift-F9。

曰く:
[bcc64 エラー] exif.cpp(586): no matching function for call to 'getTagNodePtrFromIfd'
exif.cpp(87): candidate function not viable: cannot convert argument of incomplete type 'void *' to 'IfdTable *' (aka '_ifdTable *') for 1st argument
[bcc64 エラー] exif.cpp(590): no matching function for call to 'duplicateTagNode'
exif.cpp(88): candidate function not viable: cannot convert argument of incomplete type 'void *' to 'TagNode *' (aka '_tagNode *') for 1st argument
[bcc64 エラー] exif.cpp(615): no matching function for call to 'getTagNodePtrFromIfd'
exif.cpp(87): candidate function not viable: cannot convert argument of incomplete type 'void *' to 'IfdTable *' (aka '_ifdTable *') for 1st argument
最初のものと同根のエラーですね。最初の引数を(IfdTable *)にキャストすれば良さげなので、
void *targetTag = getTagNodePtrFromIfd(ifdArray[i], tagId);
を
void *targetTag = getTagNodePtrFromIfd((IfdTable *)ifdArray[i], tagId);
と変更すればおけ。同様にキャストで対処していくと、ついにはノーエラーになります。とりあえずここで”すべて保存”しておくのが大吉です。さて、テストしてみますかね?
提供されたsample_main.cから、
// parse the JPEG header and create the pointer array of the IFD tables
ifdArray = createIfdTableArray(av[1], &result);
古典的なコマンドラインプログラムなので、av[1]は入力ファイル名です、FMXでTOpenDialogで選んだファイルだと、
void Parse(String fname)
{
void **ifdArray;
TagNodeInfo *tag;
int i, result;
ifdArray = createIfdTableArray(AnsiString(fname).c_str(),&result);
Form1->Label1->Text = IntToStr(result);
for( i = 0 ; ifdArray[i] != NULL ; i++ ){
dumpIfdTable(ifdArray[i]);
}
Form1->Memo1->Lines->Add("datetime is " + datetime);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::OpenClick(TObject *Sender)
{
if( OpenDialog1->Execute())
Parse(OpenDialog1->FileName);
}
//---------------------------------------------------------------------------
これでresultが4になりますから、大丈夫そうですね。printfをMemo1->Lines->Addへ投影する等の苦労の末、中身もおけですのようです。前半で必要なのはDateTimeのみなので、上記のようにdumpIfdTableの途中でHookして文字列を得ればよいだけです。得る側は、
extern AnsiString datetime;
.....
while (tag) {
if (Verbose) {
PRINTF(p, "tag[%02d] 0x%04X %s\n",
cnt++, tag->tagId, getTagName(ifd->ifdType, tag->tagId));
PRINTF(p, "\ttype=%u count=%u ", tag->type, tag->count);
PRINTF(p, "val=");
} else {
strcpy(tagName, getTagName(ifd->ifdType, tag->tagId));
PRINTF(p, " - %s: ", (strlen(tagName) > 0) ? tagName : "(unknown)");
}
if (tag->error) {
PRINTF(p, "(error)");
} else {
switch (tag->type) {
case TYPE_BYTE:
for (i = 0; i < (int)tag->count; i++) {
PRINTF(p, "%u ", (unsigned char)tag->numData[i]);
}
break;
case TYPE_ASCII:
if( strcmp(tagName,"DateTime") == 0 )
datetime.sprintf("%s",(char*)tag->byteData);
PRINTF(p, "[%s]", (char*)tag->byteData);
break;
case TYPE_SHORT:
for (i = 0; i < (int)tag->count; i++) {
PRINTF(p, "%hu ", (unsigned short)tag->numData[i]);
if( strcmp(tagName,"DateTime") == 0 )
datetime.sprintf("%s",(char*)tag->byteData);
を追加します。あとはsample_main.cを参考にして、
/**
* sample_removeSensitiveData()
*
* remove sensitive Exif data in a JPEG file
*
*/
int sample_removeGPSData(const char *srcJpgFileName, const char *outJpgFileName)
{
int sts, result;
void **ifdTableArray = createIfdTableArray(srcJpgFileName, &result);
if (!ifdTableArray) {
printf("createIfdTableArray: ret=%d\n", result);
return result;
}
// remove GPS IFD and 1st IFD if exist
removeIfdTableFromIfdTableArray(ifdTableArray, IFD_GPS);
removeIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
// remove tags if exist
/*
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_Make);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_Model);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_DateTime);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_ImageDescription);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_Software);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_0TH, TAG_Artist);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_MakerNote);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_UserComment);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_DateTimeOriginal);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_DateTimeDigitized);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_SubSecTime);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_SubSecTimeOriginal);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_SubSecTimeDigitized);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_ImageUniqueID);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_CameraOwnerName);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_BodySerialNumber);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_LensMake);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_LensModel);
removeTagNodeFromIfdTableArray(ifdTableArray, IFD_EXIF, TAG_LensSerialNumber);
*/
// update the Exif segment
sts = updateExifSegmentInJPEGFile(srcJpgFileName, outJpgFileName, ifdTableArray);
if (sts < 0) {
printf("updateExifSegmentInJPEGFile: ret=%d\n", sts);
}
freeIfdTableArray(ifdTableArray);
return sts;
}
のようにexif全体を消すのではなく、IFD_GPS,IFD_1STだけ消せばよいわけです。こうしておいて、
sample_removeGPSData(const char *srcJpgFileName, const char *outJpgFileName)
でおけです。ほぼできましたね。

元のC ProgramのようにExif情報のテーブルをダンプすると上図のようになります。面倒なので同じJPGをダンプしてdiffとかはしません。またstdoutではなくてTMemoにUnicodeStringを出力するのは少し工夫が必要でした。詳細はここでは省略します。
なおcppプログラムとしてまとめる以上の方法ではなく、cの関数をcppから呼び出す方策も可能ですが、修正はこちらの方が楽だと思います。cppのmanglingに対処するやり方ですね。


コメント