|
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"JPEGファイルのコメントを読み書きする"
【タイトル】JPEGファイルのコメントを読み書きする
JPEGファイルからコメントの入っているセグメントを読み出すサン
プル関数(GetComFromJPEG)と、コメントの入っているセグメントを
書き加えるサンプル関数(SetComToJPEG)です。
overload 指令を付けた同じ名前の関数が3つずつ(計6)ありま
す。それぞれ、
・ストリームを使ってコメントのセグメントを読み書きをする関数
・上記の関数を、文字列型で読み書きするようにラップした関数
・上記の関数を、引数にファイル名を渡すようにラップした関数
になっています。
overload 指令が使えないバージョンの Delphi では、それぞれの
関数名を適当に修飾すれば使えると思います。
【サンプルコード】
procedure GetComFromJPEG(Dst, Src: TStream); overload;
var
bCode: BYTE;
wData: WORD;
begin
if (Src.Read(wData, 2) = 2)and(wData = $d8ff) then
begin
while Src.Read(bCode, 1) = 1 do
begin
if bCode <> $ff then
break; // フォーマットがおかしい
// セグメント間の $ff は無視する
while Src.Read(bCode, 1) = 1 do
if bCode <> $ff then break;
// 次のセグメントが見つからなかった
if bCode = $ff then
break;
if bCode = $fe then
begin
// COM(Comment) セグメント
// 長さを取得する
if Src.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
// コメントをコピーする
Dst.CopyFrom(Src, wData - 2);
end
else if bCode in [$01, $b0..$b7] then
// これらのセグメントは長さ情報も何もない
else if bCode = $da then
// SOS(Start Of Scan) セグメント
// このセグメント以降がイメージデータ
break
else // その他のセグメントはスキップする
begin
if Src.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
Src.Seek(wData - 2, soFromCurrent);
end;
end;
end; // Code = $d8ff
end;
function GetComFromJPEG(Src: TStream): string; overload;
var
Dst: TMemoryStream;
begin
Dst := TMemoryStream.Create;
try
GetComFromJPEG(Dst, Src);
SetString(Result, PChar(Dst.Memory), Dst.Size);
finally
Dst.Free;
end;
end;
function GetComFromJPEG(FileName: string): string; overload;
var
Src: TFileStream;
begin
Src := TFileStream.Create(
FileName, fmOpenRead or fmShareDenyWrite);
try
Result := GetComFromJPEG(Src);
finally
Src.Free;
end;
end;
function SetComToJPEG(Dst, Src: TStream;
Data: Pointer; DataSize: Cardinal): Boolean; overload;
var
bCode: BYTE;
wData: WORD;
procedure WriteComSegment(WriteSize: WORD);
begin
// COM セグメントの識別コードを書き込む
wData := $feff;
Dst.Write(wData, 2);
// データの長さを書き込む
wData := WriteSize + 2;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
Dst.Write(wData, 2);
// データを書き込む
Dst.Write(PInteger(Data)^, WriteSize);
Inc(Cardinal(Data), WriteSize);
Dec(DataSize, WriteSize);
end;
begin
Result := FALSE;
if (Src.Read(wData, 2) = 2)and(wData = $d8ff) then
begin
Dst.Write(wData, 2);
// コメントを書き込む
while DataSize > $fffd do
WriteComSegment($fffd);
if DataSize > 0 then
WriteComSegment(DataSize);
while Src.Read(bCode, 1) = 1 do
begin
if bCode <> $ff then
break; // フォーマットがおかしい
// セグメント間の $ff は無視する
while Src.Read(bCode, 1) = 1 do
if bCode <> $ff then break;
// 次のセグメントが見つからなかった
if bCode = $ff then
break;
if bCode = $fe then
begin
// COM(Comment) セグメント
// 長さを取得する
if Src.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
// コメントをスキップする
Src.Seek(wData - 2, soFromCurrent);
end
else if bCode in [$01, $b0..$b7] then
begin
// これらのセグメントは長さ情報も何もない
wData := bCode * $100 + $ff;
Dst.Write(wData, 2);
end
else if bCode = $da then
begin
// SOS(Start Of Scan) セグメント
wData := bCode * $100 + $ff;
Dst.Write(wData, 2);
// このセグメント以降がイメージデータ
Dst.CopyFrom(Src, Src.Size - Src.Position);
Result := TRUE;
break;
end
else
begin
// その他のセグメントはコピーする
wData := bCode * $100 + $ff;
Dst.Write(wData, 2);
if Src.Read(wData, 2) <> 2 then
break;
Dst.Write(wData, 2);
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
Dst.CopyFrom(Src, wData - 2);
end;
end;
end; // Code = $d8ff
end;
function SetComToJPEG(
Data: TMemoryStream; Text: string): Boolean; overload;
var
Stream: TStream;
begin
Stream := TMemoryStream.Create;
try
Data.Position := 0;
Result := SetComToJPEG(Stream,Data,PChar(Text),Length(Text));
if Result then
begin
Data.Clear;
Data.CopyFrom(Stream, 0);
end;
finally
Stream.Free;
end;
end;
function SetComToJPEG(
FileName: string; Text: string): Boolean; overload;
var
Data: TMemoryStream;
S: string;
function Rename(FileName: string): string;
var
I: Integer;
S: string;
begin
Result := '';
for I := 0 to 999 do
begin
S := Format('%s%.3d', [FileName, I]);
if RenameFile(FileName, S) then
begin
Result := S;
break;
end;
end;
end;
begin
Data := TMemoryStream.Create;
try
Data.LoadFromFile(FileName);
// オリジナルファイルを改名して避難させます
S := Rename(FileName);
if (Length(S) > 0) and FileExists(S) then
begin
Data.Position := 0;
Result := SetComToJPEG(Data, Text);
// コメントを上書きしたファイルを保存する
Data.SaveToFile(FileName);
// 改名しておいたオリジナルファイルを削除する
DeleteFile(S);
end
else
Result := FALSE;
finally
Data.Free;
end;
end;
【上記の関数の使用例】
procedure TForm.Button1Click(Sender: TObject);
begin
with OpenDialog1 do
begin
title := 'JPEGファイルからコメントを取り出します';
if Execute then
begin
ShowMessage(
ExtractFileName(FileName) + 'の中のコメントは' + #13#10 +
GetComFromJPEG(FileName) + +#13#10 +
'です。');
end;
end;
end;
procedure TForm.Button2Click(Sender: TObject);
var
S: string;
begin
with OpenDialog1 do
begin
title := 'JPEGファイルにコメントを書き込みます';
if Execute then
begin
S := InputBox('ExtractFileName(FileName)','コメントは?','');
if Length(S) > 0 then
begin
if not SetComToJPEG(FileName, S) then
ShowMessage('コメントの書き込みに失敗しました');
end;
end;
end;
end;
【あとがき】
たぶん上記のコードに問題はないとは思いますが、使用される場合
には自己責任でデバッグ等を行い、必要に応じて修正してください。
2001/06/13、河邦 正(GCC02240@nifty.com)
(http://homepage2.nifty.com/kht0000/、NIFTY外へ私作のComponentの
公開用)
Original document by 河邦 正 氏 ID:(GCC02240)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|