プレビューすると文字が縮小されてしまう

[戻る]
わか [E-Mail] [HomePage] 2005/02/18(金) 16:23:14
わかです。お世話になっております。

アプリケーションウィザードで印刷および印刷プレビューを
サポートするようにしてアプリケーションを作成しました。
しかし、プレビューをすると、用紙の左上に縮小されたような
形で表示されてしまいます。印刷しても同様です。

あるサイトの過去ログを見たところ、プリンタの解像度と画面
の解像度が異なる為におこるとの解説がありました。

そこで以下の3つの方法を試してみました。
なお、すべてネットから見つけてきた情報です。

>方法1
OnDraw関数に次のコーディングを追加
if(pDC->IsPrinting()){
    pDC->SetMapMode(MM_ISOTROPIC);    
    int i = pDC->GetDeviceCaps(HORZRES);
    int j = pDC->GetDeviceCaps(VERTRES);
    pDC->SetWindowOrg(0, 0);
    pDC->SetViewportOrg(0, 0);
    pDC->SetWindowExt(500, 400);
    pDC->SetViewportExt(i, j);
}
>方法2
OnPrepareDCに以下のコードを記述する
if(pInfo && pDC->IsPrinting()){
    int    x, y;
    pDC->SetMapMode(MM_ANISOTROPIC);
    x = pDC->GetDeviceCaps(LOGPIXELSX);
    y = pDC->GetDeviceCaps(LOGPIXELSY);
    CSize s1 = pDC->SetViewportExt(x, y);
    CSize s2 = pDC->SetWindowExt(100, 100);
    pDC->OffsetWindowOrg(0, 0);
}
>方法3
OnPrepareDCに<方法2>の代わりに以下のコードを記述する
pDC->SetMapMode(MM_LOENGLISH);
 
しかしどの方法もうまくいきません。
方法1は、うまくいったように見えますが、プレビュー画面で
拡大させると、とてつもなく文字が大きく表示されます。拡大
を戻しても文字サイズは大きいままです。
方法2は、きれいに並んでいた文字(TextOutで表示)が重なっ
て表示され、文字がかなり小さく表示されます。拡大すると、
文字がやや大きくなります。拡大を戻しても、文字の大きさが
大きくなったままです。(1と同じ現象)
方法3も2と同様の結果です。

なぜ、このようなことが起きるのでしょうか?

PATIO 2005/02/18(金) 16:30:02
まず、方法をいくつか提示されていますが、
どうしてそういうことをしているのかと言う理屈は理解されていますか?
多分、そこが理解できていないとどうしてそうなるかもわからないと思いますが。

使用している関数を一つ一つ吟味していったい何をやっているのかを確認されては
どうでしょうか?
多分そこからスタートしないと対処療法で何とかしようとしても駄目だと思いますよ。

少なくともそれぞれの処理がどんな事をしてるとあなたが考えているかを書いてみてくだ
さい。
そうする事であなた自身の考えも整理されてくると思います。

わか [E-Mail] [HomePage] 2005/02/18(金) 17:27:04
PATIOさんレスありがとうございます。
では、がんばって解析してみます。間違いはご指摘ください。

とりあえず方法1の解析。
まず、pDC->IsPrinting〜は印刷時を意味してますよね?
つまり、Viewのときは通らない。(印刷時かプレビュー
時に通る。)これはいいですよね。
次にSetMapModeでMM_ISOTROPICを指定。これでxとyの
単位が同じになりますが、その単位はあとで指定します。
そして、pDC->GetDeviceCaps〜で物理ディスプレイの幅
と高さを取得しておき、SetWindowOrgとSetViewportOrg
でウィンドウの原点とビューポートの原点を0,0にします。
(これは省略できそうですね。。。)
最後にSetWindowExtでウィンドウの x 範囲と y 範囲を
設定します。(500,400はどういう根拠?)同様に先ほど
求めたディスプレイの幅と高さを、SetViewportExtを使
って設定。これで、ビューポートの x 範囲と y 範囲が
設定できるというわけですね。
OnDrawに書いているということは、常にこの判定を行って
いるということですか。

う〜ん・・・。
ヘルプの受け売りですが、自分でも意味が良くわかってい
ない用語を使ってますね。物理ディスプレイとは、PCの
画面のことでしょうか?ウィンドウとビューポートの違い
もあいまいです。まだまだ勉強不足ですね。

方法2は?
pInfo && pDC->IsPrinting())のとき。&&はANDだから、
pInfoかつ印刷時。pInfoっていうのはここでは、CPrintInfo
オブジェクトが存在するかを示しているから、つまり印刷
またはプレビュー時を意味する。(念のために入れてるの
でしょうか?なくてもいい気がしますが。) 
SetMapMode(MM_ANISOTROPIC)はMM_ISOTROPICと対称に、
任意にスケーリングされた軸上の任意の単位にマップ。
(なぜ?1ドット、1ピクセルは正方形ではないか?)
GetDeviceCaps(LOGPIXELSX)はディスプレイの幅の論理
インチごとのピクセル数を計算。その値を使ってビュー
ポートの範囲を設定。ウィンドウは100,100に設定。
(これも根拠は?)
OnPrepareDCはOnDrawまたはOnPaintが呼ばれる前に、フ
レームワークが呼び出すということは、記述する位置とし
てはこちらのほうがいい気がします。

方法3は論理単位を0.01 インチに設定するだけ。
う〜ん、なぜインチ?これは意味不明です。

これらをもう一度考察してみます。

通りすがり 2005/02/18(金) 23:41:11
フォント作成を CreatePointFont にしたらどうなりますか。

dairygoods 2005/02/21(月) 12:58:38
もっと単純なところからはじめて、
徐々に細かい調整を入れていったほうが良いのではないですか?

特に、座標属性をいじっている辺りが、
問題をややこしくしているような気がします。

たとえば、 CScrollView のスケルトンを作って、
以下のように書くだけで、画面でもプレビューでも問題なく表示されます。

注意点は、描画単位を、ピクセルでなく、
デバイスに依存しない単位(ミリメートルなど)にする点と、
y座標の向きが逆(上が正)になっていて、
画面に見えているのが、第四象限であるという点です。

void CMyView::OnInitialUpdate()
{
  CSize sizeTotal;
  // 常に 10cm x 10cm (0.1mm単位)
  sizeTotal.cx = sizeTotal.cy = 1000;
  SetScrollSizes(MM_LOMETRIC, sizeTotal);
}

void CMyView::OnDraw(CDC* pDC)
{
  // 直径10cmの円
  pDC->Ellipse(0, 0, 1000, -1000);

  // 12ポイントの文字列
  CFont font;
  font.CreatePointFont(120, "MS UI Gothic", pDC);
  CFont* oldFont = pDC->SelectObject(pDC);
  pDC->TextOut(10, -10, "こんにちは!");
  pDC->SelectObject(oldFont);
}

わか [E-Mail] [HomePage] 2005/02/21(月) 14:14:53
レスありがとうございます。

>通りすがりさん
>フォント作成を CreatePointFont にしたらどうなりますか。

これはあとで、ちょっとどうやったらよいか考えてみます。

>dairygoodsさん

これはすぐにできそうなので試してみましたが、
まず、CFont* oldFont = pDC->SelectObject(pDC);で
コンパイルエラーになりました。(error C2440)

CFont* oldFont = (CFont*)pDC->SelectObject(pDC);
としたらとりあえず通ったので、実行してみると、
こんにちは!という文字が少し変で気になります。円は
きれいに出ています。さっそくプレビューを表示してみる
とAssertionというメッセージが出ました。
なんでしょうね?デバッグ方法がまだよくわかっていない
ので、何が起きたかよくわかりません。

dairygoods 2005/02/21(月) 14:22:36
> CFont* oldFont = (CFont*)pDC->SelectObject(pDC);
> としたらとりあえず通ったので、実行してみると、

すみません、
CFont* oldFont = pDC->SelectObject(&font);
です。

わか [E-Mail] [HomePage] 2005/02/21(月) 15:00:04
>dairygoodsさん
あ、フォントだ。(シャレ言っている場合か!)今度はきれいに表示されました。
やっぱり自分で書いたコードが、どこかで悪さしているんですよね。
思い当たる部分といえば・・・、やはりフォントの設定を自分で変えているという
ところでしょうか。実はフォントダイアログを表示して、好きなフォントに変更
できるようにしてあります。起動時のデフォルトではMSゴシックにしているの
です(CreateFontIndirect)が、ここが怪しいかもしれません。
通りすがりさんのおっしゃるようにCreatePointFontにするとどうなるか実験
してみたいと思います。

ところでdairygoodsさんのサンプルのように、pDC->SelectObject(oldFont);
といったような、フォントを戻すといったことをやっていません。これが抜ける
とどのような影響でますか?

dairygoods 2005/02/21(月) 15:49:17
> ところでdairygoodsさんのサンプルのように、pDC->SelectObject(oldFont);
> といったような、フォントを戻すといったことをやっていません。これが抜ける
> とどのような影響でますか?

多くの場合、リソースがリークします。
リークしつづけると、使っているうちにリソースが枯渇し、
フォントやブラシやアイコン等が生成できなくなるため、
正常に描画できなくなります。

わか [E-Mail] [HomePage] 2005/02/21(月) 16:27:50
>通りすがりさん

LOGFONT   myLOG;
::GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &myLOG);
myLOG.lfHeight       = DEFAULT_FONT_SIZE;
myLOG.lfOutPrecision = OUT_TT_PRECIS;
myLOG.lfQuality      = DEFAULT_QUALITY;
strcpy(myLOG.lfFaceName , "MS 明朝");
myLOG.lfCharSet = SHIFTJIS_CHARSET;
myFONT.CreateFontIndirect(&myLOG);

最後の行のところを、
CClientDC myDC(this);
myFONT.CreatePointFont(120,myLOG.lfFaceName,&myDC);
と置き変えてみましたが、変わりありませんでした。
(ここでmyFONTは CFont myFONT; としてpublicで定義済み)

>dairygoodsさん
>多くの場合、リソースがリークします。

上記コードは、コンストラクタに記述しています。
CreateFontIndirectにせよ、CreatePointFontにせよ
後処理は必要なのでしょうか?

dairygoods 2005/02/21(月) 17:19:58
> 上記コードは、コンストラクタに記述しています。
> CreateFontIndirectにせよ、CreatePointFontにせよ
> 後処理は必要なのでしょうか?

提示されたコード片からだけでは判断できませんが、
プログラム全体を検証して実際にリークする場合だけ
後処理するというつもりでしたら、
その様なアプローチは、お勧めできません。

#得られる効果 < ソースコードの作成や維持に掛かるコスト

常に元に戻してください。

わか [E-Mail] [HomePage] 2005/02/21(月) 18:31:32
[[解決]]
できました!!
フォントを戻す処理を追加したらうまくいきました。
CreateFontIndirectはコンストラクタに書いて、
OnDrawの最初と最後でフォント変更、フォントを戻す
処理をいれてやったらうまくいきました。
(いままでは、戻す処理がなく、フォントの変更だけしていた)

// フォントの変更(と同時に古いフォントを保存)
CFont* oldFont = pDC->SelectObject(&myFONT);

(中略)←TextOutなど描画処理

// 使い終わったフォントを古いフォントに戻す
pDC->SelectObject(oldFont);

リークとかいう話になってくると、いかにもC言語ですね。
ここは常に意識しないといけませんね。
どうもありがとうございました。

古いスレッドには追加発言できません。