12

メタファイル(EMF)を生成するアプリケーションがあります。参照デバイス(別名画面)を使用してこれらのメタファイルをレンダリングするため、メタファイルのDPIは、コードが実行されているマシンに応じて変化します。

私のコードが8.5インチx11インチのメタファイルを作成しようとしているとしましょう。開発ワークステーションを参照として使用すると、EMFが次のようになります。

  • {0、0、21590、27940}のrclFrame(1000分の1 mm単位のメタファイルの寸法)
  • {1440、900}のszlDevice(参照デバイスの寸法(ピクセル単位))
  • {416、260}のszlMillimeters(参照デバイスの寸法(mm))

さて、rclFrameはEMFのサイズが

  • 21590/2540=幅8.5
  • 27940/2540=高さ11

右に。この情報を使用して、計算が正しければ、モニターの物理DPIも決定できます。

  • (1440 * 25.4)/ 416=87.9231水平dpi
  • (900 * 25.4)/ 260=87.9231垂直dpi

問題

このメタファイルを再生するもの(EMFからPDFへの変換、WindowsエクスプローラーでEMFを右クリックしたときの[概要]ページなど)は、計算されたDPI値を切り捨てて、87.9231ではなく87を表示するようです( 88で十分です)。

これにより、メタファイルの再生時に、物理的なサイズが8.5インチx 11インチ(88 dpiを使用)ではなく、8.48インチx 10.98インチ(87 dpiを使用)になります。

  • DPIの計算に使用されるメタファイルに格納されている情報が適切な整数になるように、参照デバイスのDPIを変更することはできますか?
  • 独自のデバイスコンテキストを作成してそのDPIを指定できますか?それとも、それを行うために本当にプリンターを使用する必要がありますか?

洞察をありがとう。

4

4 に答える 4

16

私は今、メタファイルについて知っていた以上のことを学びました。

1.Metafileクラスのコンストラクターのオーバーロードの一部は正常に機能せず、切り捨てられたDPI値で動作します。

次のことを考慮してください。

protected Graphics GetNextPage(SizeF pageSize)
{
    IntPtr deviceContextHandle;
    Graphics offScreenBufferGraphics;
    Graphics metafileGraphics;
    MetafileHeader metafileHeader;

    this.currentStream = new MemoryStream();
    using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
    {
        deviceContextHandle = offScreenBufferGraphics.GetHdc();
        this.currentMetafile = new Metafile(
            this.currentStream,
            deviceContextHandle,
            new RectangleF(0, 0, pageSize.Width, pageSize.Height),
            MetafileFrameUnit.Inch,
            EmfType.EmfOnly);

        metafileGraphics = Graphics.FromImage(this.currentMetafile);

        offScreenBufferGraphics.ReleaseHdc();
    }

    return metafileGraphics;
}

{8.5、11}のを渡した場合、{21590、27940}のを取得することをSizeF期待できます。結局のところ、インチからミリメートルへの変換は難しくありません。しかし、あなたはおそらくそうしないでしょう。解像度にもよりますが、GDI +は、インチパラメータを変換するときに切り捨てられたDPI値を使用するようです。それを正しくするために、私は100分の1ミリメートルでそれを自分で行う必要があります。これは、GDI +がメタファイルヘッダーにネイティブに格納される方法であるため、通過するだけです。MetafilerclFrame

this.currentMetafile = new Metafile(
    this.currentStream,
    deviceContextHandle,
    new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
    MetafileFrameUnit.GdiCompatible,
    EmfType.EmfOnly);

丸め誤差#1が解決されました-rclFrameメタファイルのが正しくなりました。

Graphics2.に記録するインスタンスのDPIMetafileは常に間違っています。

メタファイルmetafileGraphicsを呼び出して設定した変数を参照してください。Graphics.FromImage()そうですね、そのGraphicsインスタンスのDPIは常に96dpiになるようです。(推測する必要がある場合は、物理的なDPIではなく、常に論理的なDPIに設定されます。)

Graphics96 dpiで動作しているインスタンスを描画しMetafile、ヘッダーに87.9231dpiが「記録」されているインスタンスに記録しているときに生じる陽気さを想像してみてください。(他の値から計算されるため、「記録された」と言います。)メタファイルの「ピクセル」(メタファイルに格納されているGDIコマンドはピクセルで指定されていることを忘れないでください)は大きいので、何かを描画するための呼び出しを呪い、つぶやきます1インチの長さは、1インチを超える長さになります。

Graphics解決策は、インスタンスを縮小することです。


metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
    metafileHeader.DpiX / metafileGraphics.DpiX,
    metafileHeader.DpiY / metafileGraphics.DpiY);

それはやっかいではありませんか?しかし、それはうまくいくようです。

「丸め」エラー#2は解決しました。88dpiで「1インチ」で何かを描くと言うと、そのピクセルは$%$^の方がよいでしょう。ピクセル#88として記録されます。

3.szlMillimeters大きく変化する可能性があります。リモートデスクトップは非常に楽しいものです。

そのため、(Markの回答によると)WindowsがモニターのEDIDを照会し、実際にモニターの物理的な大きさを認識していることを発見しました。GDI +HORZSIZEは、プロパティを入力するときにこれ(など)を有効に使用しszlMillimetersます。

ここで、家に帰ってこのリモートデスクトップのコードをデバッグするとします。自宅のコンピューターに16:9のワイドスクリーンモニターがあるとします。

明らかに、WindowsはリモートディスプレイのEDIDを照会できません。そのため、従来のデフォルトである320 x 240 mmを使用します。これは、アスペクト比が4:3であることを除けば問題ありません。現在、まったく同じコードが、ディスプレイ上にメタファイルを生成しています。正方形の物理ピクセル:水平DPIと垂直DPIは異なり、最後にそれが発生したのを思い出せません。

今のところ、これに対する私の回避策は、「まあ、リモートデスクトップで実行しないでください」です。

rclFrame4.使用していたEMF-to-PDFツールで、ヘッダーを見るときに丸め誤差が発生しました。

これが、この質問を引き起こした私の問題の主な原因でした。私のメタファイルはずっと「正しい」ものであり(最初の2つの問題を修正した後は正しい)、「高解像度」のメタファイルを作成するためのこの検索はすべて赤ニシンでした。低解像度のディスプレイデバイスでメタファイルを記録すると、ある程度の忠実度が失われるのは事実です。これは、メタファイルで指定されているGDIコマンドがピクセル単位で指定されているためです。ベクトル形式であり、スケールアップまたはスケールダウンできるかどうかは関係ありません。実際の記録中に、 GDI +が操作をスナップする「ピクセル」を決定すると、一部の情報が失われます。

ベンダーに連絡したところ、修正されたバージョンが提供されました。

丸め誤差#3が解決されました。

5. Windowsエクスプローラーの[概要]ペインは、計算されたDPIを表示するときに値を切り捨てます。

この切り捨てられた値が、EMF-to-PDFツールが内部で使用していたのと同じ誤った値を表しているのは偶然です。これを除けば、この癖は議論に意味のあるものを何も提供しません。

結論

私の質問はデバイスコンテキストでのDPIの使用に関するものだったので、Mark'sは良い答えです。

于 2009-10-07T17:27:19.757 に答える
3

Windowsがモニターの物理的なサイズをどのように認識しているかについて知りたいです。どこかで設定を変更したに違いありませんか?おそらく、うまく分割できるより便利な値に変更できます。

名前が示すように、「デバイスコンテキスト」はシステムデバイスに接続する必要があります。ただし、これはハードウェアドライバーである必要はなく、PDFライターの印刷ドライバーなどのデバイスエミュレーターである可能性があります。任意のDPIを設定できるものを少なくとも1つ見ました。

于 2009-10-06T20:57:55.003 に答える
0

概要ページの値が間違っているようです。それらは次のように計算されます:

Size = round(precize_size)+1
Resolution = trunc(precize_resolution)

正確な値が丸めや切り捨てなしで計算される場合。

于 2011-11-22T14:36:18.103 に答える
0

私は常にWXPで120dpi(大きなフォント)で実行していることに注意してください。これは、metafileGraphics.DpiXが120を返すことを意味します。

EMFファイルは、参照コンテキストのdpiが何であったかを記録していないようです(この場合は120、他のほとんどの人は96)。

さらに面白くするために、SetResolution()がたとえば300dpiに設定されているメモリ内ビットマップに描画することでEMFを作成することができます。その場合、スケーリング係数は300である必要があり、モニター(86.x)またはWindows(120)が使用しているものではないと思います。

于 2010-07-21T16:07:52.023 に答える