喫茶XRAYトップページ


[6551] メタファイルとマッピング返信 削除
2012/12/15 (土) 20:18:49 taki
マスター教えてください。

長い複数ページにわたる文章を一度にCanvasに書き込んで、
任意の範囲(ページ)をプレビューしたり印刷したりしたいのです。

マスターのplPrevを使用すれば解決なのは分かっているのですが。

マスターのサンプル154,172とか
http://www.geocities.jp/asumaroyuumaro/program/tips/Printer.html
を参考にして考えました。

procedure TForm1.Button1Click(Sender: TObject);
var
  DocMeta :TMetafile;
  PageMeta :TMetafile;
  DocMetaCanvas :TMetafileCanvas;
  PageMetaCanvas :TMetafileCanvas;
  PrintRect :TRect;
  i,j :Integer;
  sx_dpm,sy_dpm:real;
  PageTate,PageYoko:integer;  // A4の有効範囲270mm、180mm
begin
//  スクリーンの解像度
  sx_dpm := screen.PixelsPerInch / 25.4;
  sy_dpm := screen.PixelsPerInch / 25.4;   // 1mm当りのピクセル数

// A4有効印刷範囲  210 × 297 1ページのサイズ
  PageTate := round(270 * sy_dpm);   // ピクセル
  PageYoko := round(180 * sx_dpm);

//テスト用メタファイルを作成
  DocMeta  :=TMetaFile.Create;
  PageMeta :=TMetaFile.Create;
  try
    DocMeta.Width  := PageYoko;         // Screen解像度
    DocMeta.Height := PageTate * 2;     // 2ページ分
    j := (PageTate * 2) div 20;         // 印字する数字の最大値
    label1.Caption := inttostr(j);      // 102

    DocMetaCanvas := TMetafileCanvas.Create(DocMeta, 0);
    try
      for i := 1 to j do begin
        DocMetaCanvas.TextOut(50, i * 20, inttostr(i));
      end;
    finally
      DocMetaCanvas.Free;
    end;

// ここまででDocMetaに2ページにわたる文章(数字)が書き込まれた。

    PageMetaCanvas := TMetafileCanvas.Create(PageMeta, 0);  // Dest
    try
      SetMapMode(PageMetaCanvas.Handle,MM_ANISOTROPIC);
      SetWindowExtEx(PageMetaCanvas.Handle,PageYoko,PageTate,nil);
      SetViewPortExtEx(PageMetaCanvas.Handle,PageYoko,PageTate,nil); // 倍率1

//     SetWindowOrgEx(PageMetaCanvas.Handle,0,0,nil);  ←1ページ目が出力 1,2,…
      SetWindowOrgEx(PageMetaCanvas.Handle,0,PageTate,nil);  // 2ページ目 51,52,…

      PageMetaCanvas.Draw(0,0,DocMeta);

    finally
      PageMetaCanvas.Free;
    end;

// 以上でPageMetaに1ページ分だけ切り出した

// プレビュー画面
    Image2.Canvas.Draw(0,0,PageMeta);  // 原寸大で

// プリンタへの印刷
    PrintRect :=Bounds(GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX),
                       GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY),
                       GetDeviceCaps(Printer.Handle, HORZRES),
                       GetDeviceCaps(Printer.Handle, VERTRES));

    Printer.Title := 'MetaTest';
    Printer.BeginDoc;
    try
      Printer.Canvas.StretchDraw(PrintRect,PageMeta);
//      Printer.Canvas.Draw(0,0,PageMeta);   ←ものすごく小さく印字される
    except
      Printer.Abort;
      raise;
    end;
    Printer.EndDoc;

  finally
    DocMeta.Free;
    PageMeta.Free;
  end;

end;

大まかな処理の説明です。
1.数字1〜102を書き出したDocMetaメタファイルを作成しています。
2.マッピングでDocMetaファイルから1ページ分をPageMetaファイルに切り取ります。
3.PageMetaファイルをプレビュー用のImage1に表示
4.同時にプリンタへ出力しています。

疑問点・問題点
1.メタファイルのサイズをスクリーンの解像度に合わせたのですが問題はないでしょうか。
2.マッピングでSetWindowExtEx,SetViewPortExtEx,SetWindowOrgExが
すべてPageMetaCanvasに対して行われているのが理解できない。
DocMetaCanvasだったらなんとなく理解できますが。
3.プリンタへの印刷がPrinter.Canvas.Draw(0,0,PageMeta);では
ものすごく小さく印字される。
仕方がないのでPrinter.Canvas.StretchDrawを使うが文字が歪んでしまう。


[6554] Re:メタファイルとマッピング返信 削除
2012/12/16 (日) 00:32:37 Mr.XRAY
▼ takiさん

今,非常に眠いので,
流し読みで気が付いたところだけ.

メタファイルを作成してから,また別のメタファイルを作成している
ようなのですが.う〜ん,必要あるのかな ?

それと,マッピングですが,TplPrev では,0.1mm単位で描画するために
使っているので,既にテキストを書き込んだメタファイルを作成している
のであれば不要かと.

>3.プリンタへの印刷がPrinter.Canvas.Draw(0,0,PageMeta);では
>ものすごく小さく印字される。


これは,そういうものですね.
基本的には,StretchDraw です.

>仕方がないのでPrinter.Canvas.StretchDrawを使うが文字が歪んでしまう。

これは,最初にテキストを書き込むメタファイルの設定ではないかと
思いますが.また....
Asuyu さんのところに,いいサンプルがあるんですね.


[6555] Re:メタファイルとマッピング返信 削除
2012/12/16 (日) 00:37:04 Mr.XRAY
> マスターのサンプル154,172とか

印刷ということが目的でしたら,
どちらかと言うと,こちらの方がいいかも知れません.
[テストプログラム  ScallingDraw]
http://mrxray.on.coocan.jp/Delphi/plSetPrinter/ScallingDraw.htm


[6556] Re2:メタファイルとマッピング返信 削除
2012/12/16 (日) 08:27:33 taki
▼ Mr.XRAYさん

お疲れのところすみません。m(_ _)m

今までは文章のプレビューと印刷をページ単位で逐次生成して
なんとかプログラムしていたのですが、今回印刷のレイアウトが
複雑になり、文書全体のレイアウトを仕上げるだけで手いっぱいに
なってしまいました。

最初の発想はImage1.Canvas.TextOut()で長い文章を出力して
その一部分をImage2.Canvas.CopyRect()で切り取ったら
任意の位置(ページ)の文章をプレビューできると思ったことです。

しかし、そのままでは印刷はできない(非常に小さくなる)ので、
以前から気になっていたメタファイルを使おうと考えました。

「メタファイル 切り取り」とかのキーワードで検索したら、
結局マスターの所へ来てしまいました。

マッピングは本来はスケールや座標軸等の変更に使われるものだと
わかったのですが、今回はメタファイルからの切り取る方法として
使っています。

>基本的には,StretchDraw です.

ということなので、その方向で考えます。

マッピングで、受け取る側のメタファイルのキャンバスに
色々と条件を設定して
DestCanvas.Draw(0,0,SourceMeta);が不思議ですね。
まあ、そういう物なのだと納得します。

「ScallingDraw」のページも立ち寄っていました。

ありがとうございます。<(_ _)>


[6557] Re3:メタファイルとマッピング返信 削除
2012/12/16 (日) 11:15:58 Mr.XRAY
▼ takiさん

> 最初の発想はImage1.Canvas.TextOut()で長い文章を出力して
> その一部分をImage2.Canvas.CopyRect()で切り取ったら
> 任意の位置(ページ)の文章をプレビューできると思ったことです。


なるほど.

> しかし、そのままでは印刷はできない(非常に小さくなる)ので、
> 以前から気になっていたメタファイルを使おうと考えました。


ですね.アイディアとしては,いいのではないかと思われます.

> マッピングは本来はスケールや座標軸等の変更に使われるものだと

です.ハイ.多分.

> >基本的には,StretchDraw です.

メタファイルの場合,うまく説明できないのですが,本質的にビットマップ
等でいうところの「サイズ」というのがなく,ベクタ画像なので,
サイズというのは,実際は「表示した時のサイズ」と考えた方が
いいのではないかと思います.
もちろん,ディスクに保存した時の「サイズ」はありますが.

> DestCanvas.Draw(0,0,SourceMeta);が不思議ですね。
> まあ、そういう物なのだと納得します。


ハハハッ.

> 「ScallingDraw」のページも立ち寄っていました。

このサンプルコードの ACanvasをメタファイルの Canvas に出力する
方法も考えられますね.
ただ,今回の兆戦は,結構難しい部類になるような気もします.


[6558] Re4:メタファイルとマッピング返信 削除
2012/12/16 (日) 23:08:37 taki
▼ Mr.XRAYさん

> ただ,今回の兆戦は,結構難しい部類になるような気もします.

マスターの予告どおり、なかなか思うようにいきません。

マッピングの処理の意味がなんとなく分かってきました。

ビットマップのようなイメージの場合は切り取り処理をすると
切り取った範囲だけになるのでファイルサイズは減少しますが、
メタファイルの場合は、元のデータを全て持った状態で
切り取った範囲の部分だけ見えるように穴を開けているようです。

元のソースのメタファイルのサイズが933kに対して、切り取った
メタファイルのサイズが1.3Mもあります。
マスク?の分が増えたと想像します。

だから、受け取り側のメタファイルのキャンバスに対して
マッピング処理をしているのですね。(T_T)


[6559] Re5:メタファイルとマッピング返信 削除
2012/12/17 (月) 14:01:09 taki
▼ Mr.XRAYさん

マッピングを使ってSourceMetaからDestMetaを切り取った場合、
DestMetaは
  Image1.AutoSize := True;
  Image1.Picture.Assign(DestMeta);
の方法でしか正しく表示しないようです。

特に、Image1.AutoSize := True; は大切で、こうすることにより
余計な部分の表示をクリッピングして(隠して)いるようです。

Image1.Canvas.Draw(20,20,DestMeta); とか

PrintRect := Bounds(20,20,DestMeta.Width,DestMeta.Height);
Image1.Canvas.StretchDraw(PrintRect,DestMeta) とかすると、

DestMetaのサイズ以外の余白部分まで前後左右のデータがImage1に
表示されてしまいます。

例えば、1ページが50行で前後にいくらかの余白を持たせる場合、
2ページ目を表示すると、べたっとデータで埋め尽くされます。

Image1の上端------------------------
49行目のデータ (本来は上の余白)
50行目のデータ ( 〃 )
51行目のデータ ここからが2ページ目の内容


100行目のデータ ここまでが2ページ目の内容
101行目のデータ (本来は下の余白)
102行目のデータ ( 〃 )
Image1の下端------------------------

のように余白部分にまでデータが書き込まれてしまうのです。

これは、メタファイルとはこういうものなんだと諦めるしか
ないようです。(T_T)


[6560] Re6:メタファイルとマッピング返信 削除
2012/12/17 (月) 17:10:15 おかぽん
余計な部分の描画を抑えたいということであれば・・・

CreateRectRgnで1ページ分の矩形範囲のリージョンを作成
SelectClipRgnで、キャンバスにクリッピングリージョンを設定する。

メタファイルの描画を行い

再度SelectClipRgnで、クリッピングリージョンを削除し
DeleteObjectで、リージョンを廃棄する。

コードで書けばこんな感じ
var
  hrgn:Thandle;
bgein
  hrgn := CreateRectRgn(20,20,200,250);
  SelectClipRgn(Image1.Canvas.Handle, hrgn);
  //ここでImage1.Canvasにメタファイル描画
  SelectClipRgn(Image1.Canvas.Handle, 0);
  DeleteObject(hrgn);
end;

試していないので、できるかわかりませんけど。

[6561] Re7:メタファイルとマッピング返信 削除
2012/12/17 (月) 17:43:47 taki
▼ おかぽんさん

ありがとうございます。
試してみます。


[6562] Re7:メタファイルとマッピング返信 削除
2012/12/17 (月) 19:27:42 taki
▼ おかぽんさん

Image1へのクリッピングは簡単にできました。

さらに、プリンターへの出力のクリッピングもうまくできました。

こんな方法があるのですね。
大変勉強になりました。 (^_^)

[6563] Re7:メタファイルとマッピング返信 削除
2012/12/17 (月) 19:47:06 Mr.XRAY
▼ おかぽんさん

>CreateRectRgnで1ページ分の矩形範囲のリージョンを作成
>SelectClipRgnで、キャンバスにクリッピングリージョンを設定する。


なるほどね.
ホントに,よくまぁ,考えつくもんですね.


INCM/CMT
Cyclamen v3.82