お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
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