2019/01/23
 ファイルの上書き保存モードが無かったので追加しました。

マルチTiffファイルの作成と編集

 複数ページのTiffファイルの作成と、ページの追加、削除、取り出しを行うプログラムです。
GDI+のTiffファイルの機能を利用します。

 色々な資料の保存に、古いWindowsのコダックのImagingを使用していましたが無くなってしまいました。
 32ビットのXPや7の場合、著作権の問題はありましたが、Windows2000のインストールCDを利用してインストールして使用することも可能でした。
しかし、最近ではプログラムが使用するメモリーの容量も多くなり、64ビットのOSを使用するのが一般的となりコダックのImagingは使用できなくなりました。
 フリーのソフトで、MultiTiffの扱えるのもあるのですが、画像の編集をすることは殆ど無く、単にファイルの編集だけなので、自作することにしてみました。
フリーソフトでは、IrfanViewがあります、インターネット検索すれば、ダウンロードサイトが直ぐに見つかるでしょう。
 自作と言っても、基本的には、Delphi Library [Mr.XRAY]02_GDI+ を使用したマルチページ TIFF 画像の作成と表示 を元にして作成をしました。
 ページの編集時、追加削除挿入を簡単に行うため、メモリーストリームを使用するように変更しています。
 マルチTiffのイメージに対して単に追加する場合は、追加保存だけでよいのですが、挿入、削除の場合は、前のTiffeを新しいTiffイメージに1ページ目から保存し指定のページになったら挿入の場合は、新しい画像を追加後、最後のページまで保存し、削除の場合は指定のページはスキップして最後のページまで保存することで、挿入削除を行います。
編集は、メモリーストリームを使用し、HDは使用しないので、高速に行えます。
最近のPCはメモリーが大量に載っているので問題ないでしょう。
 スキャナーからの画像が取り込めると便利なので、TWAIN機器からの画像取り込みも追加してみました。
 TWAIN用のプログラムは、インターネットで"DelphiTwain"で検索と出てくるKluug.net Software Solutions からダウンロードしました。


 プログラムは、MakeTiffボタンで編集の開始をします。
このボタンをクリックすると、次のダイアログが開きます。


 新規を選ぶと、新しいTiffファイルの作成です、この場合は、最初 AddFileボタンでも画像の追加をします。
追加するものは、ファイルからでもよいし、スキャナーからでも追加が出来ます。
Tiffファイルを選んだ場合は、元々あるTiffファイルのページ編集です、1ページしか無いものでも、マルチページのTiffファイルでも可能です。

 AddFileボタンは、最後のページとして追加されます。

Insertボタンは指定されたページの前にページが挿入されます。

マルチTiffファイルを追加、或いは挿入すると、マルチTiffファイルのページ分 新しいページとして追加されます。
追加、挿入の画像ファイルの形式は、GDI+で扱える画像形式なら何でも良いのですが、Gif形式のファイルは、動画の場合、画像が複数あるのですが、最初の画像しか使用されません。

Deleteボタンは指定されたページの削除です、1ページしか無い場合は、単にボタンの設定と、画像の消去、新規であるフラグ再設定を行います。

 マルチページの一部のページが必要な場合は、表示中の画像をファイルとして保存するボタンも設定してありますが、プログラムは、Tiffファイルのみです。
GDI+を使用しているので、GDI+ファイル変換プログラムを参照して追加すれば、色々な形式での保存が追加できるようになります。

 何処かのページの画像の編集が必要な場合は、一度編集の必要なページを保存して、そのファイルを画像編集ソフトで編集後、元のページに挿入、元のページを削除すれば良い事になります。
今まで、マルチTiffファイルの画像の編集は行ったことが無いので、不必要かと思います。

 FileOpenボタンは、単にマルチTiffファイルの表示をするためのものです。
Windowsには、標準で マルチTiffファイルの表示が出来るソフトが組み込まれているので、本来必要ないのですが、参考の為に組み込んでみました。

Twain用のユニットは、プロジェクトの保存ホルダーと同じホルダーに保存しておけばOKです。
違うホルダーに入れた場合は、ツールオプションの設定でライブラリィパスを通す必要があります。

プログラム

//----------------------------------------------------------------------
// DelphiTwain  download http://www.kluug.net/delphitwain.php
//----------------------------------------------------------------------

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  System.UITypes, Graphics, Vcl.Controls, Vcl.Forms,
  Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, GDIPAPI, GDIPOBJ, GDIPUTIL, Vcl.ExtDlgs,
  Vcl.Buttons, Vcl.ComCtrls, DelphiTwain, DelphiTwain_Vcl, Vcl.Imaging.pngimage;

type
  TForm1 = class(TForm)
    OpenBtn: TButton;
    OpenPictureDialog1: TOpenPictureDialog;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Edit1: TEdit;
    Label1: TLabel;
    DownBtn: TButton;
    UpBtn: TButton;
    MakeTiffBtn: TButton;
    AddFileBtn: TButton;
    SaveFileBtn: TButton;
    CancelBtn: TButton;
    Edit2: TEdit;
    Label2: TLabel;
    DeleteBtn: TButton;
    Edit3: TEdit;
    Label3: TLabel;
    InsertBtn: TButton;
    CheckBox1: TCheckBox;
    Image2: TImage;
    ListBox1: TListBox;
    BtnScanWithDialog: TButton;
    Image3: TImage;
    FsaveDispbtn: TButton;
    SavePictureDialog1: TSavePictureDialog;
    Label4: TLabel;
    OverWriteBtn: TButton;
    FileCloseBtn: TButton;
    procedure OpenBtnClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure DownBtnClick(Sender: TObject);
    procedure UpBtnClick(Sender: TObject);
    procedure MakeTiffBtnClick(Sender: TObject);
    procedure SaveFileBtnClick(Sender: TObject);
    procedure AddFileBtnClick(Sender: TObject);
    procedure CancelBtnClick(Sender: TObject);
    procedure DeleteBtnClick(Sender: TObject);
    procedure InsertBtnClick(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure BtnScanWithDialogClick(Sender: TObject);
    procedure FsaveDispbtnClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure OverWriteBtnClick(Sender: TObject);
    procedure FileCloseBtnClick(Sender: TObject);
  private
    Twain: TDelphiTwain;
    function TWainCreat: Boolean;
    procedure TwainTwainAcquire(Sender: TObject; const Index: Integer; Image: TBitmap; var Cancel: Boolean);
    procedure ReloadSources;
    procedure Scanerin;
    procedure PageDisp(P: integer);
    procedure FirstWork(Fname: string);
    procedure AddWork(FaddS: TMemoryStream; LTiif: boolean);
    procedure DeletePage(Page: integer);
    procedure insertimge(InsS : TMemoryStream; ipage: integer; LTiff: boolean);
    procedure FirstStream(FrtS : TMemoryStream);
    procedure FirstEncode;
    function MyMessageBox(const Msg: String): integer;
    function StartMessageBox(const Msg: String): integer;
    procedure DispImge(P: integer);
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  LGPImage        : TGPImage;
  LPageCount      : Integer = 0;
  LPageDimensions : TGPSizeF;
  LPageGuid       : TGUID;
  FPage           : Integer = -1;

  LEncoderParams      : TEncoderParameters;
  LEncoderParamValue  : TEncoderValue;
  LclsidTIFF          : TGUID;
  LGPImageTIFF      : TGPImage;
  Fname             : string;
  Exstr             : string;
  LFirstTiff        : boolean;
  FFileoutStream    : TMemoryStream;
  FFileinpStream    : TMemoryStream;

  LGPImageDisp      : TGPImage;
  InsertF           : Boolean;
  InsertP           : Integer;
  ChangF : Boolean;
  OverWF : Boolean;

//-------------------------------------------
// スタート選択メッセージボックス
//-------------------------------------------
function TForm1.StartMessageBox(const Msg: String): integer;
var
  Dlg: TForm;
begin
   Dlg := CreateMessageDialog(Msg, mtConfirmation, [mbYes, mbNo, mbCancel]);
   with Dlg do
   try
     Caption := '編集方法の選択';
     TImage(Components[0]).Picture.Assign(Image3.Picture);
//     TImage(Components[0]).Picture.LoadFromFile(Filename);
     TButton(Components[2]).Caption := '新規';             // ret 6   mrYes
     TButton(Components[3]).Caption := 'TiFFファイル';     // ret 7   mrNo
     TButton(Components[4]).Caption := '取り消し';         // ret 2   mrCancel

     result := ShowModal;                                  // クローズ 2 mrCancel
   finally
     Free;
   end;
end;

//-------------------------------------------
// ソース選択メッセージボックス
//-------------------------------------------
function TForm1.MyMessageBox(const Msg: String): integer;
var
  Dlg: TForm;
begin
   Dlg := CreateMessageDialog(Msg, mtConfirmation, [mbYes, mbNo, mbCancel]);
   with Dlg do
   try
     Caption := 'ソースの選択';
     TImage(Components[0]).Picture.Assign(Image2.Picture);
//     TImage(Components[0]).Picture.LoadFromFile(Filename);
     TButton(Components[2]).Caption := 'ファイル';           // ret 6  mrYes
     TButton(Components[3]).Caption := 'スキャナー';         // ret 7  mrNo
     TButton(Components[4]).Caption := '取り消し';           // ret 2  mrCancel

     result:= ShowModal;                                     // クローズ 2 mrCancel
   finally
     Free;
   end;
end;

//--------------------------------------
// Twainデバイスの一覧表示
//--------------------------------------
procedure TForm1.ReloadSources;
var
  I: Integer;
begin
  ListBox1.Items.Clear;
  for I := 0 to Twain.SourceCount - 1 do
    ListBox1.Items.Add(Twain.Source[I].ProductName);

  if ListBox1.Items.Count > 0 then
    ListBox1.ItemIndex := 0;
end;

//------------------------------------
// Twainデバイスの取得
//------------------------------------
function TForm1.TWainCreat: Boolean;
begin
  if Twain <> nil then FreeAndNil(Twain);
  Twain := TDelphiTwain.Create;
  // Twain画像取得割り込み設定
  Twain.OnTwainAcquire := TwainTwainAcquire;
  // Twainデバイスライブライの取得
  if Twain.LoadLibrary then
  begin
    // Load source manager
    Twain.SourceManagerLoaded := TRUE;

    ReloadSources;
    result := True;
  end else begin
    ShowMessage('Twain is not installed.');
    result := False;
  end;
end;

//---------------------------------------------------
// Twain画像の取得割り込み処理
// Cancel := True で取得終了
// Image:   取得画像
// Index:   取得画像Index
// Cancel:  画像取得打ち切りフラグ true で打ち切り
//--------------------------------------------------
procedure TForm1.TwainTwainAcquire(Sender: TObject; const Index: Integer; Image: TBitmap; var Cancel: Boolean);
var
  FBitmapStream     : TMemoryStream;
begin
  FBitmapStream := TMemoryStream.Create;
  try
    Image.SaveToStream(FBitmapStream);
    if not InsertF then begin
      if not LFirstTiff then begin
        FirstStream(FBitmapStream);               // 最初のページ作成
        SaveFileBtn.Enabled := True;
        LFirstTiff := True;                       // 最初のページ作成フラグセット
      end
      else
        AddWork(FBitmapStream, False);            // ページの追加
    end
    else
      insertimge(FBitmapStream, InsertP, False);  // ページの挿入
  finally
    FBitmapStream.Free;
  end;
  Cancel := True;                                 // Only want one image

  if LPageCount > 0 then begin
    insertbtn.Enabled := True;
    deleteBtn.Enabled := True
  end
  else begin
    insertbtn.Enabled := False;
    deleteBtn.Enabled := False;
  end;
end;

//------------------------------
// Twainデバイスでの画像取得開始
//------------------------------
procedure TForm1.Scanerin;
begin
  if not TWainCreat then begin
    BtnScanWithDialog.Enabled := False;
    exit;
  end;
  BtnScanWithDialog.Enabled := True;
end;

//-------------------------------------
// 指定されたTwinデバイスで画像の取得
//-------------------------------------
procedure TForm1.BtnScanWithDialogClick(Sender: TObject);
begin
  Twain.SelectedSourceIndex := ListBox1.ItemIndex;
  if Assigned(Twain.SelectedSource) then begin
    // Load source, select transference method and enable (display interface)}
    Twain.SelectedSource.Loaded := True;
    Twain.SelectedSource.ShowUI := True;  // display interface
    Twain.SelectedSource.Enabled := True;
  end;
  BtnScanWithDialog.Enabled := False;
end;

//--------------------------------------------------
// Tiffファイル編集中の表示画面Tiffファイル保存
//--------------------------------------------------
procedure TForm1.FsaveDispbtnClick(Sender: TObject);
var
  Oname: string;
begin
  if LPageCount < 1 then exit;
  SavePictureDialog1.Filter := 'すべて (*.tif;*.tiff)|*.tif;*.tiff'
                             + '|TIFF 画像 (*.tif) |*.tif'
                             + '|TIFF 画像 (*.tiff)|*.tiff';
  if not SavePictureDialog1.Execute then exit;
  Oname := SavePictureDialog1.FileName;
  Exstr := LowerCase(ExtractFileExt(Oname));
  if (Exstr = '.tif') or (Exstr = '.tiff') then else
    Oname := ChangeFileExt(Oname,'.tif');
  if FileExists(Oname) then         // ファイル名によるファイル検索
    if MessageDlg('既に同じ名前のファイルがあります上書きしますか ' + ExtractFileName(Oname) + '?',
                                                      mtConfirmation, [mbYes, mbNo], 0, mbNo) = mrNo then exit;
  LEncoderParamValue := EncoderValueFrameDimensionPage;
  LGPImageDisp.Save(Oname, LclsidTIFF, @LEncoderParams)
end;

//----------------------------------
// 画像追加削除挿入後の表示
// P が負数だったら最後のページ表示
//----------------------------------
procedure TForm1.DispImge(P: integer);
begin
  // 画面表示設定
  // 解放にFreeAndNilを使用しないと Nil に設定されない場合があります
  if LGPImageDisp <> nil then FreeAndNil(LGPImageDisp);
  // 出力用メモリーストリームから画像生成
  LGPImageDisp := TGPImage.Create(TStreamAdapter.Create(FFileoutStream, soReference));
  LPageGuid := FrameDimensionPage;
  LPageCount := LGPImageDisp.GetFrameCount(LPageGuid);
  // 表示ページ最終ページ目にセット
  if P < 0 then
    FPage := LPageCount - 1
  else
    FPage := P;
  // ページ数表示
  Edit1.Text := intTostr(Fpage + 1) + ' / ' + intTostr(LPageCount);
  Edit2.Text := intTostr(Fpage + 1);
  Edit3.Text := intTostr(Fpage + 1);
  PageDisp(FPage);
end;

//--------------------
// エンコード指定
//--------------------
procedure TForm1.FirstEncode;
begin
  MakeTiffBtn.Enabled := False;
  // ここではTIFFへのエンコードだけ指定
  LEncoderParams.Count := 1;
  LEncoderParams.Parameter[0].Guid           := EncoderSaveFlag;
  LEncoderParams.Parameter[0].Type_          := EncoderParameterValueTypeLong;
  LEncoderParams.Parameter[0].NumberOfValues := 1;
  LEncoderParams.Parameter[0].Value          := @LEncoderParamValue;
  // TIFFのGUIDを取得
  GetEncoderClsid('image/tiff', LclsidTIFF);
end;

//----------------------------------------------------------
// 最初の画像ストリームをTiff形式でメモリーストリームに保存
//----------------------------------------------------------
procedure TForm1.FirstStream(FrtS : TMemoryStream);
begin
  // エンコード指定
  FirstEncode;
  // 出力用メモリーストリーム生成
  if FFileoutStream <> nil then FreeAndNil(FFileoutStream);
  FFileoutStream := TMemoryStream.Create;
  if LGPImage <> nil then FreeAndNil(LGPImage);
  // LGPImageにメモリーストリームから画像生成
  LGPImage := TGPImage.Create(TStreamAdapter.Create(FrtS, soReference));
  // パラメータマルチページに設定
  LEncoderParamValue := EncoderValueMultiFrame;
  // 1ページ目を出力用メモリーストリームに保存 出力先 出力形式 エンコードパラメーター の指定
  LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
  // 最初のページ画像表示
  DispImge(0);
  ChangF := true;
  if OverWF then OverWritebtn.Enabled := True;
end;

//----------------------------------------------------------
// 最初の画像ファイルをTiff形式でメモリーストリームに保存
// 読み込み用ファイルストリームは省略可能です
//----------------------------------------------------------
procedure TForm1.FirstWork(Fname: string);
var
  i : integer;
begin
  // FFileinpStreamに入力ファイルが関連付けされていたら解放
  if FFileinpStream <> nil then FreeAndNil(FFileinpStream);
  FFileinpStream := TMemoryStream.Create;
  // メモリーストリームにファイル読み込み
  FFileinpStream.LoadFromFile(Fname);
  // エンコード指定
  FirstEncode;
  // 出力用メモリーストリーム生成
  if FFileoutStream <> nil then FreeAndNil(FFileoutStream);
  FFileoutStream := TMemoryStream.Create;
  // LGPImageに入力画像が生成されていたら解放
  // 解放にFreeAndNilを使用しないと Nil に設定されない場合があります
  if LGPImage <> nil then FreeAndNil(LGPImage);
  // LGPImageに入力ファイル画像生成
  LGPImage := TGPImage.Create(TStreamAdapter.Create(FFileinpStream, soReference));
  // 入力ファイルの拡張子の取り出し
  Exstr := LowerCase(ExtractFileExt(Fname));
  LPageCount := 1;
  // Tiff だったら
  if (Exstr = '.tif') or (Exstr = '.tiff') then begin
    // Tiffのページ設定取得用
    LPageGuid := FrameDimensionPage;
    // ページ数取得
    LPageCount := LGPImage.GetFrameCount(LPageGuid);
    // 1ページ目をアクティブに
    LGPImage.SelectActiveFrame(LPageGuid, 0);
    // パラメータマルチページに設定
    LEncoderParamValue := EncoderValueMultiFrame;
    // 1ページ目を出力用メモリーストリームに保存 出力先 出力形式 エンコードパラメーター の指定
    LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
    // 複数ページだったら追加保存
    if LPageCount > 1 then begin
      for i := 1 to LPageCount - 1 do begin
        LGPImage.SelectActiveFrame(LPageGuid, i);
        // 選択ページのページ状態の書き込み設定  追加ページ
        LEncoderParamValue := EncoderValueFrameDimensionPage;
        // 追加保存 出力元 エンコードパラメーター の指定 追加の時は出力先の指定は不要
        LGPImage.SaveAdd(LGPImage, @LEncoderParams);
      end;
    end;
    // 最初の開始がTifファイルだったら上書きフラグセット
    OverWF := True;
  end
  // Tiffファイル以外だったら
  else begin
    // パラメータマルチページに設定
    LEncoderParamValue := EncoderValueMultiFrame;
    // メモリーストリームにTiff形式として保存
    LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
    ChangF := true;
  end;
  // 最後のページ画像表示
  DispImge(LPageCount - 1);
  FsaveDispbtn.Enabled := True;
  // 必要がなくなったので解放
  FreeAndnil(FFileinpStream);
end;

//---------------------------------------------
// 画像メモリストリームの追加
// 最後のページに追加します挿入はありません
//---------------------------------------------
procedure TForm1.AddWork(FaddS: TMemoryStream; LTiif: boolean);
var
  i : integer;
begin
  // メモリストリーから画像生成
  LGPImageTIFF := TGPImage.Create(TStreamAdapter.Create(FaddS, soReference));
  // Tiff ファイルだったら
  if LTiif then begin
    try
      // Tiffのページ設定取得用
      LPageGuid := FrameDimensionPage;
      // ページ数取得
      LPageCount := LGPImageTIff.GetFrameCount(LPageGuid);
      // ページ分出力用メモリーストリームに追加保存
      for i := 0 to LPageCount - 1 do begin
        // アクティブページの設定
        LGPImageTIFF.SelectActiveFrame(LPageGuid, i);
        // 選択ページのページ状態の書き込み設定 追加ページ
        LEncoderParamValue := EncoderValueFrameDimensionPage;
        // 追加保存
        LGPImage.SaveAdd(LGPImageTIFF, @LEncoderParams);
      end;
    finally
      // LGPImageTIFFの解放
      LGPImageTIFF.Free;
    end;
  end
  // Tiff 以外だったら
  else begin
    try
      // 選択ページのページ状態の書き込み設定 追加設定
      LEncoderParamValue := EncoderValueFrameDimensionPage;
      // 追加保存 最初の Saveで出力先が設定されているので出力先は不要です
      LGPImage.SaveAdd(LGPImageTIFF, @LEncoderParams);
    finally
      // LGPImageTIFFの解放
      LGPImageTIFF.Free;
    end;
  end;
  // 最後のページ画像表示
  DispImge(-1);
  ChangF := true;
  if OverWF then OverWritebtn.Enabled := True;
end;

//-----------------------------------------
// 指定ページの削除
//-----------------------------------------
procedure TForm1.DeletePage(Page: Integer);
var
  I, P            : integer;
  FDelStream      : TMemoryStream;
begin
  // 1ページしかなかったら初期化し終了
  if LPageCount = 1 then begin
    LFirstTiff := False;
    LPageCount := 0;
    DeleteBtn.Enabled := False;
    SaveFileBtn.Enabled := False;
    FsaveDispbtn.Enabled := False;
    Image1.Canvas.Brush.Color := clBtnface;
    Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
    Edit1.Text := '';
    Edit2.Text := '';
    Edit3.Text := '';
    ChangF := False;
    exit;
  end;
  // 2ページ以上あったら指定ページ削除
  FDelStream := TMemoryStream.Create;
  FDelStream.SetSize(0);
  // FDelStreamに FFileoutStreamをコピー
  FDelStream.CopyFrom(FFileoutStream, 0);
  // FFileoutStreamクリア
  FFileoutStream.Clear;
  if LGPImage <> nil then FreeAndNil(LGPImage);
  // LGPImageに入力ファイルストリーム関連付け
  LGPImage := TGPImage.Create(TStreamAdapter.Create(FDelStream, soReference));
  try
    LPageGuid := FrameDimensionPage;
    // ページ数取得
    LPageCount := LGPImage.GetFrameCount(LPageGuid);
    // 1ページ目をアクティブに
    if Page = 1 then P := 1 else P := 0;
    LGPImage.SelectActiveFrame(LPageGuid, P);
    // パラメータマルチページに設定
    LEncoderParamValue := EncoderValueMultiFrame;
    // 1ページ目を出力用メモリーストリームに保存 出力先 出力形式 エンコードパラメーター の指定
    LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
    // 選択ページのページ状態の書き込み設定 追加設定
    LEncoderParamValue := EncoderValueFrameDimensionPage;
    // 複数ページだったら追加保存
    if LPageCount > 1 then begin
      for I := P + 1 to LPageCount - 1 do begin
        // 指定ページ以外追加保存
        if (I + 1) <> Page then begin
          LGPImage.SelectActiveFrame(LPageGuid, I);
          // 追加保存 出力元 エンコードパラメーター の指定 追加の時は出力先の指定は不要
          LGPImage.SaveAdd(LGPImage, @LEncoderParams);
        end;
      end;
    end;
  finally
    FDelStream.Free;
  end;
  // 削除した次のページ画像表示
  DispImge(Page - 1);
  ChangF := true;
  if OverWF then OverWritebtn.Enabled := True;
end;

//--------------------------------------------------
// 指定ページに画像メモリストリーム挿入
//--------------------------------------------------
procedure TForm1.insertimge(InsS : TMemoryStream; ipage: integer; LTiff: boolean);
var
  I, J            : integer;
  FAddStream      : TMemoryStream;
  Jpagecount      : integer;
begin
  // FADDStreamメモリーストリーム生成
  FAddStream := TMemoryStream.Create;
  FAddStream.SetSize(0);
  // FADDStreamに FFileoutStreamをコピー
  FAddStream.CopyFrom(FFileoutStream, 0);
  // 出力用メモリーストリームクリア
  FFileoutStream.Clear;
  if LGPImage <> nil then FreeAndNil(LGPImage);
  // 挿入ページ1ページ目だったら
  if ipage = 1 then begin
    LGPImage := TGPImage.Create(TStreamAdapter.Create(InsS, soReference));
    // Tiff ファイルだったら
    if LTiff then begin
      // Tiffのページ設定取得用
      LPageGuid := FrameDimensionPage;
      // ページ数取得
      Jpagecount := LGPImage.GetFrameCount(LPageGuid);
      // 1ページ目をアクティブに
      LGPImage.SelectActiveFrame(LPageGuid, 0);
      // パラメータマルチページに設定
      LEncoderParamValue := EncoderValueMultiFrame;
      // 1ページ目を出力用メモリーストリームに保存 出力先 出力形式 エンコードパラメーター の指定
      LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
      // 複数ページだったら追加保存
      // 選択ページのページ状態の書き込み設定 追加ページ
      LEncoderParamValue := EncoderValueFrameDimensionPage;
      if Jpagecount > 1 then begin
        for I := 1 to Jpagecount - 1 do begin
          LGPImage.SelectActiveFrame(LPageGuid, I);
          // 追加保存 出力元 エンコードパラメーター の指定 追加の時は出力先の指定は不要
          LGPImage.SaveAdd(LGPImage, @LEncoderParams);
        end;
      end;
    end
    // Tiffファイル以外だったら
    else begin
      // パラメータマルチページに設定
      LEncoderParamValue := EncoderValueMultiFrame;
      // メモリーストリームにTiff形式として保存
      LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
    end;
    // LGPImageTIFFにメモリーストリームから画像生成
    LGPImageTIFF := TGPImage.Create(TStreamAdapter.Create(FAddStream, soReference));
    try
      LPageGuid := FrameDimensionPage;
      // ページ数取得
      LPageCount := LGPImageTIFF.GetFrameCount(LPageGuid);
      // 選択ページのページ状態の書き込み設定  追加ページ
      LEncoderParamValue := EncoderValueFrameDimensionPage;
      // ページ分出力用メモリーストリームに追加保存
      for I := 0 to LPageCount - 1 do begin
        // アクティブページの設定
        LGPImageTIFF.SelectActiveFrame(LPageGuid, I);
        // 追加保存
        LGPImage.SaveAdd(LGPImageTIFF, @LEncoderParams);
      end;
    finally
      // LGPImageTIFFの関連付け解放
      LGPImageTIFF.Free;
      FAddStream.Free;
    end;
  end
  // 1ページ目以降だったら
  else begin
    // LGPImageにメモリーストリームから画像生成
    LGPImage := TGPImage.Create(TStreamAdapter.Create(FAddStream, soReference));
    LGPImageTIff := TGPImage.Create(TStreamAdapter.Create(InsS, soReference));
    try
      // Tiffのページ設定取得用
      LPageGuid := FrameDimensionPage;
      // ページ数取得
      LPageCount := LGPImage.GetFrameCount(LPageGuid);
      // 1ページ目をアクティブに
      LGPImage.SelectActiveFrame(LPageGuid, 0);
      // パラメータマルチページに設定
      LEncoderParamValue := EncoderValueMultiFrame;
      // 1ページ目を出力用メモリーストリームに保存 出力先 出力形式 エンコードパラメーター の指定
      LGPImage.Save(TStreamAdapter.Create(FFileoutStream, soReference), LclsidTIFF, @LEncoderParams);
      for I := 1 to LPageCount - 1 do begin
        // 選択ページのページ状態の書き込み設定 追加ページ
        LEncoderParamValue := EncoderValueFrameDimensionPage;
        // 挿入ページと一致したら
        if I + 1 = ipage then begin
          // Tiffファイルなら
          if LTiff then begin
            // Tiffのページ設定取得用
            LPageGuid := FrameDimensionPage;
            // ページ数取得
            Jpagecount := LGPImageTIff.GetFrameCount(LPageGuid);
            for J := 0 to Jpagecount - 1 do begin
              // 選択ページをアクティブに
              LGPImageTIff.SelectActiveFrame(LPageGuid, J);
              // 追加保存
              LGPImage.SaveAdd(LGPImageTIff, @LEncoderParams);
            end;
          end
          // Tiff以外なら
          else begin
            // 挿入保存
            LGPImage.SaveAdd(LGPImageTIFF, @LEncoderParams);
          end;
        end;
        // アクティブページの設定
        LGPImage.SelectActiveFrame(LPageGuid, I);
        // 追加保存
        LGPImage.SaveAdd(LGPImage, @LEncoderParams);
      end;
    finally
      LGPImageTIFF.free;
      FAddStream.Free;
    end;
  end;
  // 追加ページの画像表示
  DispImge(ipage - 1);
  ChangF := true;
  if OverWF then OverWritebtn.Enabled := True;
end;

//-------------------------------------
// Tiff ファイル作成編集キャンセル
// 初期状態に戻します
//-------------------------------------
procedure TForm1.CancelBtnClick(Sender: TObject);
begin
  if MessageDlg('本当にキャンセルしますか' +#13#10 + '編集内容は全て失われます。',
                                                      mtConfirmation, [mbYes, mbNo], 0, mbNo) = mrNo then exit;

  CancelBtn.Enabled := False;
  // 出力用メモリーストリーム解放
  if FFileoutStream <> nil then FreeAndNil(FFileoutStream);
  // TGPImage解放 LGPImage
  if LGPImage <> nil then FreeAndNil(LGPImage);
  // TGPImage解放 LGPImageDisp
  if LGPImageDisp <> nil then FreeAndNil(LGPImageDisp);
  // Twain機器の解放
  if Twain <> nil then FreeAndNil(Twain);
  // 画像表示消去
  Edit1.Text := '';
  Edit2.Text := '';
  Image1.Canvas.Brush.Color := clBtnFace;
  Image1.Canvas.FillRect(Image1.Canvas.ClipRect);

  AddFileBtn.Enabled := False;
  SaveFileBtn.Enabled := False;
  OpenBtn.Enabled := True;
  MakeTiffBtn.Enabled := True;
  DownBtn.Enabled := False;
  UpBtn.Enabled   := False;
  DeleteBtn.Enabled := False;
  InsertBtn.Enabled := False;
  BtnScanWithDialog.Enabled := False;
  FsaveDispbtn.Enabled := False;
  OverWritebtn.Enabled := False;

  FPage := -1;
  LPageCount := -1;
  Form1.Caption := 'MakeTiff';
  ChangF := False;
end;

//--------------------------------------------------------------------------
const

  LFilter =  'すべて (*.gif;*.jpg;*.jpeg;*.png;*.bmp;*.tif;*.tiff;*.ico)|*.gif;*.jpg;*.jpeg;*.png;*.bmp;*.tif;*.tiff;*.ico'
           + '|GIF 画像 (*.gif)|*.gif'
           + '|JPEG イメージファイル (*.jpg)|*.jpg'
           + '|JPEG イメージファイル (*.jpeg)|*.jpeg'
           + '|Portable Network Graphics (*.png)|*.png'
           + '|ビットマップ (*.bmp)|*.bmp'
           + '|TIFF 画像 (*.tif)|*.tif'
           + '|TIFF 画像 (*.tiff)|*.tiff'
           + '|アイコン (*.ico)|*.ico';

//---------------------------------------------------
// 新しいマルチTiffファイルの作成
// Tiff ファイルを指定するとそのファイルのページ編集
//---------------------------------------------------
procedure TForm1.MakeTiffBtnClick(Sender: TObject);
var
  ST : integer;
begin
  Fname := '';
  // 最初のTiffフラグ解除
  LFirstTiff := False;
  ST := StartMessageBox('Tiff編集の選択');   // Tiff画像ファイルか新規か選択
  if ST = 2 then exit;                       // キャンセルなら終了
  if ST = 7 then begin                       // Tiffファイルだったら
    OpenPictureDialog1.Filter := 'すべて (*.tif;*.tiff)|*.tif;*.tiff'
                               + '|TIFF 画像 (*.tif) |*.tif'
                               + '|TIFF 画像 (*.tiff)|*.tiff';
    if not OpenPictureDialog1.Execute then exit;      // キャンセルなら終了
    Fname := OpenPictureDialog1.FileName;
    Exstr := LowerCase(ExtractFileExt(Fname));
    if (Exstr = '.tif') or (Exstr = '.tiff') then     // Tiffファイル確認
      // 既にあるTiffファイルなら最初のTiffフラグセット
      if FileExists(Fname) then LFirstTiff := True;
    if LFirstTiff then begin
      FirstWork(Fname);                       // 編集開始
      SaveFileBtn.Enabled := True;
      Form1.Caption := 'MakeTiff  ' + Fname;
    end
    else exit;                                // Tiff以外のファイルだったら終了
  end;
  if ST = 6 then begin                        // 新規なら
    Form1.Caption := 'MakeTiff  新規';
    LPageCount := 0;
  end;
  MakeTiffBtn.Enabled := False;
  AddFileBtn.Enabled := True;                 // 追加ボタン
  OpenBtn.Enabled := False;
  Cancelbtn.Enabled := True;                  // キャンセルボタン
  insertbtn.Enabled := True;
  if LPageCount > 0 then begin                // ページが有ったら
    insertbtn.Enabled := True;                // 挿入ボタン
    deleteBtn.Enabled := True                 // 削除ボタン
  end
  else begin
    insertbtn.Enabled := False;
    deleteBtn.Enabled := False;
  end;
end;

//--------------------------------------
// 新しいマルチTiffファイルの保存
//--------------------------------------
procedure TForm1.SaveFileBtnClick(Sender: TObject);
var
  LSaveFile       : string;
begin
  SavePictureDialog1.Filter := 'すべて (*.tif;*.tiff)|*.tif;*.tiff'
                             + '|TIFF 画像 (*.tif) |*.tif'
                             + '|TIFF 画像 (*.tiff)|*.tiff';
  if not SavePictureDialog1.Execute then exit;
  Fname := SavePictureDialog1.FileName;
  Exstr := LowerCase(ExtractFileExt(Fname));
  if (Exstr = '.tif') or (Exstr = '.tiff') then
    LSaveFile := Fname
  else
    LSaveFile := ChangeFileExt(Fname,'.tif');
  if FileExists(LSaveFile) then         // ファイル名によるファイル検索
    if MessageDlg('既に同じ名前のファイルがあります上書きしますか ' + ExtractFileName(LSaveFile) + '?',
                                                      mtConfirmation, [mbYes, mbNo], 0, mbNo) = mrNo then exit;
  // 指定されたファイル名で保存
  FFileoutStream.SaveToFile(LSaveFile);
  Form1.Caption := 'MakeTiff  ' + LSaveFile;
  Fname := LSaveFile;
  ChangF := false;

//  CancelBtnClick(nil);
end;

//-------------
// 上書き保存
//-------------
procedure TForm1.OverWriteBtnClick(Sender: TObject);
var
  BackupFile : string;
begin
  if FileExists(Fname) and ChangF then  // ファイル名によるファイル検索
    if MessageDlg('本当に上書きしますか ' + ExtractFileName(Fname) + '?',
      mtConfirmation, [mbYes, mbNo], 0, mbNo) = IDNo then exit
    else begin
      BackupFile := ChangeFileExt(Fname, '.bak');
      CopyFile(PWideChar(Fname), PWideChar(BackupFile), false);
      FFileoutStream.SaveToFile(Fname);
    end;
  ChangF := false;
  OverWritebtn.Enabled := False;
end;

//-----------------------
// Tiffに新しい画像追加
//-----------------------
procedure TForm1.AddFileBtnClick(Sender: TObject);
var
  Ret   : integer;
  AddS  : TMemoryStream;
  LTiff : Boolean;
begin
  InsertF := False;
  BtnScanWithDialog.Enabled := False;
  Ret := MyMessageBox('ファイルかスキャナー選択');
  if Ret = 2 then exit;
  if Ret = 7 then begin
    scanerin;                  // スキャナーから画像取得追加
    exit;
  end;

  OpenPictureDialog1.Filter := LFilter;
  if not OpenPictureDialog1.Execute then exit;
  // 最初の画像のTiffがメモリーストリームに作成されていなかったら作成
  if not LFirstTiff then begin
    FirstWork(OpenPictureDialog1.FileName);
    LFirstTiff := True;
  end
  // 作成されてたら追加
  else begin
    Exstr := LowerCase(ExtractFileExt(OpenPictureDialog1.FileName));
    LTiff := False;
    if (Exstr = '.tif') or (Exstr = '.tiff') then LTiff := True;
    AddS := TMemoryStream.Create;
    try
      AddS.LoadFromFile(OpenPictureDialog1.FileName);
      AddWork(AddS, LTiff);
    finally
      AddS.Free;
    end;
  end;
  SaveFileBtn.Enabled := True;
  if LPageCount > 0 then begin
    insertbtn.Enabled := True;
    deleteBtn.Enabled := True
  end
  else begin
    insertbtn.Enabled := False;
    deleteBtn.Enabled := False;
  end;
  ChangF := true;
  if OverWF then OverWritebtn.Enabled := True;
end;

//--------------------------
// 指定ページの削除
//--------------------------
procedure TForm1.DeleteBtnClick(Sender: TObject);
var
  check : integer;
  page  : integer;
begin
  val(Edit2.Text, Page, check);
  if (check <> 0) or (Page < 1) or (page > LPageCount) then begin
    application.MessageBox('入力された値に間違いがあります。','注意',0);
    exit;
  end;
  if MessageDlg('本当に指定した' + intTostr(Page) + 'ページを削除しますか ?',
                                                      mtConfirmation, [mbYes, mbNo], 0, mbNo) = mrNo then exit;
  DeletePage(Page);                 // 指定ページ削除
  if LPageCount > 0 then begin
    insertbtn.Enabled := True;
    deleteBtn.Enabled := True
  end
  else begin
    insertbtn.Enabled := False;
    deleteBtn.Enabled := False;
  end;
end;

//------------------------------
// 指定ページに画像挿入
//------------------------------
procedure TForm1.InsertBtnClick(Sender: TObject);
var
  ipage, Ret  : integer;
  Check       : integer;
  LTiff       : Boolean;
  InsS        : TMemoryStream;
begin
  InsertF := True;
  val(Edit3.Text, iPage, check);
  if (check <> 0) or (iPage < 1) or (ipage > LPageCount) then begin
    application.MessageBox('入力された値に間違いがあります。','注意',0);
    exit;
  end;
  BtnScanWithDialog.Enabled := False;
  InsertP := iPage;
  Ret := MyMessageBox('ファイルかスキャナー選択');
  if Ret = 2 then exit;
  if Ret = 7 then begin
    scanerin;         // スキャナーから画像取得挿入
    exit;
  end;
  // ファイルから画像挿入
  OpenPictureDialog1.Filter := LFilter;
  if not OpenPictureDialog1.Execute then exit;
  Exstr := LowerCase(ExtractFileExt(OpenPictureDialog1.FileName));
  LTiff := False;
  if (Exstr = '.tif') or (Exstr = '.tiff') then LTiff := True;
  InsS := TMemoryStream.Create;
  try
    InsS.LoadFromFile(OpenPictureDialog1.FileName);
    Insertimge(InsS, ipage, LTiff);
  finally
    InsS.Free;
  end;
end;

//==================================================================
//----------------------------------
// Tiffファイルの画像表示
// ページ指定で表示 0~LPageCount - 1
//----------------------------------
procedure TForm1.PageDisp(P: integer);
var
  W, H        : Integer;
  HH, WW, WH  : double;
  LGPGraphic  : TGPGraphics;
begin
  if P < 0 then exit;
  DownBtn.Enabled := False;
  UpBtn.Enabled   := False;
  Image1.Canvas.Brush.Color := clBtnface;
//  Image1.Canvas.Brush.Color := clWhite;
  Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
  LGPImageDisp.SelectActiveFrame(LPageGuid, P);
  LGPImageDisp.GetPhysicalDimension(LPageDimensions);
  // 各ページの画像のサイズ
  W := Trunc(LPageDimensions.Width);
  H := Trunc(LPageDimensions.Height);
  if CheckBox1.Checked then begin
    WW := (ScrollBox1.Width  - 5) / W;
    HH := (ScrollBox1.Height - 5) / H;
    if (WW < 1) or (HH < 1) then begin
      if WW < HH then WH := WW
                 else WH := HH;
      W := Trunc(W * WH);
      H := Trunc(H * WH);
    end;
  end;
  Image1.width  := W;
  Image1.Height := H;
  Image1.Picture.Bitmap.SetSize(W, H);
  Image1.Update;
  // Image1のサイズ変更するとHandleが変わるのでサイズ変更後取得します
  LGPGraphic := TGPGraphics.Create(Image1.Canvas.Handle);
  try
    // 表示 MakeRectを使用すると変倍描画が可能となります。
    LGPGraphic.DrawImage(LGPImageDisp, MakeRect(0, 0, W, H));
//    LGPGraphic.DrawImage(LGPImageDisp, 0, 0, W, H);
    Image1.Repaint;                                        // 描画の更新
  finally
    LGPGraphic.Free;
  end;
  if LPageCount > 1 then begin
    DownBtn.Enabled := True;
    UpBtn.Enabled   := True;
  end;
end;

//---------------------------------------
// 表示を終了と画像の消去
//---------------------------------------
procedure TForm1.FileCloseBtnClick(Sender: TObject);
begin
  if LGPImageDisp <> nil then freeAndNil(LGPImageDisp);
  Image1.Canvas.Brush.Color := clBtnFace;
  Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
  MakeTiffBtn.Enabled := True;
  FileCloseBtn.Enabled := False;
  OpenBtn.Enabled := True;
  DownBtn.Enabled := False;
  UpBtn.Enabled := False;
  Form1.Caption := 'MultiTiff';
end;

//------------------------------------
// Tiff ファイルの読み込み表示用
// 表示のみです
//------------------------------------
procedure TForm1.OpenBtnClick(Sender: TObject);
begin
  OpenPictureDialog1.Filter := 'すべて (*.tif;*.tiff)|*.tif;*.tiff'
                              + '|TIFF 画像 (*.tif) |*.tif'
                              + '|TIFF 画像 (*.tiff)|*.tiff';
  if not OpenPictureDialog1.Execute then exit;
  OpenBtn.Enabled := False;

  // 解放にFreeAndNilを使用しないと Nil に設定されない場合があります
  if LGPImageDisp <> nil then freeAndNil(LGPImageDisp);
  // FFileinpStreamに入力ファイルが関連付けされていたら解放
  if FFileinpStream <> nil then FreeAndNil(FFileinpStream);
  FFileinpStream := TMemoryStream.Create;
  // メモリーストリームにファイル読み込み
  FFileinpStream.LoadFromFile(OpenPictureDialog1.FileName);
  // LGPImageに入力ファイルストリーム関連付け
  LGPImageDisp := TGPImage.Create(TStreamAdapter.Create(FFileinpStream, soReference));
  // ページ定数セット ページ設定取得用
  LPageGuid := FrameDimensionPage;
  // ページ数取得
  LPageCount := LGPImageDisp.GetFrameCount(LPageGuid);
  // 表示ページ1ページ目にセット
  FPage := 0;
  // ページ数表示
  Edit1.Text := '1 / ' + intTostr(LPageCount);
  // 指定ページ表示
  PageDisp(FPage);
  DownBtn.Enabled := True;
  UpBtn.Enabled   := True;
  insertbtn.Enabled := False;
  deleteBtn.Enabled := False;
  MakeTiffBtn.Enabled := False;
  FileCloseBtn.Enabled := True;
  Form1.Caption := 'Open Tiff  ' + OpenPictureDialog1.FileName;
end;

//-------------------------------
// 表示ページダウン
//-------------------------------
procedure TForm1.DownBtnClick(Sender: TObject);
begin
  if Fpage > 0 then begin
    dec(Fpage);
    Edit1.Text := intTostr(Fpage + 1) + ' / ' + intTostr(LPageCount);
    Edit2.Text := intTostr(Fpage + 1);
    Edit3.Text := intTostr(Fpage + 1);
    PageDisp(Fpage);
  end;
end;

//----------------------------
// 表示ページアップ
//----------------------------
procedure TForm1.UpBtnClick(Sender: TObject);
begin
  if Fpage < LPageCount - 1 then begin
    inc(Fpage);
    Edit1.Text := intTostr(Fpage + 1) + ' / ' + intTostr(LPageCount);
    Edit2.Text := intTostr(Fpage + 1);
    Edit3.Text := intTostr(Fpage + 1);
    PageDisp(Fpage);
  end;
end;

//---------------------------------------
// チェックでスクロールバー無しで枠内表示
//---------------------------------------
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if Fpage >= 0 then PageDisp(Fpage);
end;

//-------------
// 初期設定
//-------------
procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.Top  := 0;
  Image1.Left := 0;
  DownBtn.Enabled := False;
  UpBtn.Enabled   := False;
  AddFileBtn.Enabled := False;
  SaveFileBtn.Enabled := False;
  Cancelbtn.Enabled := False;
  DeleteBtn.Enabled := False;
  insertbtn.Enabled := False;
  BtnScanWithDialog.Enabled := False;
  FsaveDispbtn.Enabled := False;
  OverWritebtn.Enabled := False;
  FileCloseBtn.Enabled := False;
end;

// 終了前処理
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if ChangF then
    if application.MessageBox('変更されています保存せず終了しますか ?','注意', MB_YESNO) = idNo then
                  CanClose := false;
end;

//------------
// 終了処理
//------------
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if FFileoutStream <> nil then FreeAndNil(FFileoutStream);
  if LGPImage <> nil then FreeAndNil(LGPImage);
  if LGPImageDisp <> nil then FreeAndNil(LGPImageDisp);
  if Twain <> nil then FreeAndNil(Twain);
  if FFileinpStream <> nil then FreeAndNil(FFileinpStream);
end;

end.

    download GdipMultiTIFF.zip

画像処理一覧へ戻る

      最初に戻る