領収書を生成しています。Graphicsオブジェクトを使用してDrawStringメソッドを呼び出し、必要なテキストを出力しています。
graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);
これは、私がそれを実行するために必要なことに対してうまく機能しました。何を印刷するのかは常にわかっていたので、80mmのレシート用紙に適切に収まるように、手作業で文字列をトリミングできました。次に、これをより柔軟にする機能を少し追加する必要がありました。ユーザーは、下部に追加される文字列を渡すことができます。
彼らが何を置くのかわからなかったので、ラップする文字と文字列自体を取り込む独自のWordラップ関数を作成しました。文字数を調べるために、私は次のようなことをしていました:
float width = document.DefaultPageSettings.PrintableArea.Width;
int max = (int)(width / graphics.MeasureString("a", font).Width);
これで、幅は283を返します。これはmmで約72です。これは、80mm用紙のマージンを考慮する場合に意味があります。
しかし、MeasureStringメソッドはCourier New 8ptフォントで10.5を返します。したがって、私が36〜40であると予想していたものを回避する代わりに、26を取得し、その結果、2行のテキストが3〜4に変わります。
PrintableArea.Widthの単位は1/100インチであり、グラフィックスオブジェクトのPageUnitはDisplayです(これは通常、プリンターでは1/100インチです)。では、なぜ26しか返されないのですか?
WindowsClient.netから:
GDI +は、表示されるすべての文字列の両端に少量(1/6 em)を追加します。この1/6 emは、端がオーバーハングしているグリフ(イタリック 'f'など)を許可し、グリッドフィッティングの拡張に役立つ少しの余裕をGDI +に与えます。
DrawString
のデフォルトアクションは、隣接する実行を表示する際に無効になります。
- まず、デフォルトのStringFormatは、各出力の両端に1/6 emを追加します。
- 次に、グリッドにフィットした幅が設計よりも小さい場合、文字列は最大emまで縮小できます。
これらの問題を回避するには:
- 文字列のStringFormat(
MeasureString
)に基づいて、常にDrawString
およびGenericTypographic
にStringFormatを渡します。
グラフィックスTextRenderingHint
をTextRenderingHintAntiAlias
に設定します。このレンダリング方法は、アンチエイリアスとサブピクセルグリフ配置を使用してグリッドフィッティングの必要性を回避するため、本質的に解像度に依存しません。
.NETでテキストを描画する方法は2つあります。
graphics.MeasureString
およびgraphics.DrawString
)TextRenderer.MeasureText
およびTextRenderer.DrawText
)Michael Kaplanの(rip)優れたブログ Sorting It All Out 、.NET 1.1では、すべてがテキストレンダリングにGDI +を使用しました。しかし、いくつかの問題がありました:
- GDI +のややステートレスな性質によって引き起こされるパフォーマンスの問題がいくつかあります。デバイスコンテキストが設定され、各呼び出しの後に元の状態が復元されます。
- 国際テキストの整形エンジンは、Windows/UniscribeおよびAvalon(Windows Presentation Foundation)で何度も更新されていますが、GDI +では更新されていないため、新しい言語の国際レンダリングサポートの品質は同じではありません。
したがって、彼らは.NETフレームワークを変更してGDI +のテキストレンダリングシステムを停止し、を使用することを望んでいることを知っていました[〜#〜] gdi [〜#〜]。最初、彼らは単に変更できることを望んでいました:
graphics.DrawString
gDI +の代わりに古いDrawText
APIを呼び出す。しかし、テキストの折り返しと間隔をGDI +のように正確に一致させることはできませんでした。そのため、GDI +を呼び出すためにgraphics.DrawString
を維持することを余儀なくされました(互換性の理由。graphics.DrawString
を呼び出していた人々は、テキストが以前のように折り返されていないことに突然気づきました)。
GDIテキストレンダリングをラップするために、新しい静的TextRenderer
クラスが作成されました。これには2つのメソッドがあります:
TextRenderer.MeasureText
TextRenderer.DrawText
注:
TextRenderer
はGDIのラッパーですが、graphics.DrawString
はGDI +のラッパーです。
次に、既存のすべての.NETコントロールをどうするかという問題がありました。
Label
Button
TextBox
彼らはTextRenderer
(つまりGDI)を使用するように切り替えたいと思っていましたが、注意が必要でした。 .NET 1.1の場合のように、コントロールの描画に依存していた人もいるかもしれません。そして、「互換性のあるテキストレンダリング」が誕生しました。
デフォルトでは、アプリケーションのコントロールは.NET 1.1の場合と同様に動作します(「compatible」です)。
以下を呼び出すことにより、互換モードをオフにします。
Application.SetCompatibleTextRenderingDefault(false);
これにより、アプリケーションの国際化サポートが向上し、より速く、より速くなります。総括する:
SetCompatibleTextRenderingDefault(true) SetCompatibleTextRenderingDefault(false)
======================================= ========================================
default opt-in
bad good
the one we don't want to use the one we want to use
uses GDI+ for text rendering uses GDI for text rendering
graphics.MeasureString TextRenderer.MeasureText
graphics.DrawString TextRenderer.DrawText
Behaves same as 1.1 Behaves *similar* to 1.1
Looks better
Localizes better
Faster
GDIフォント描画に使用されるGDI + TextRenderingHint
と対応する LOGFONT
Quality の間のマッピングに注意することも役立ちます:
TextRenderingHint mapped by TextRenderer to LOGFONT quality
======================== =========================================================
ClearTypeGridFit CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit ANTIALIASED_QUALITY (4)
AntiAlias ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit PROOF_QUALITY (2)
SingleBitPerPixel DRAFT_QUALITY (1)
else (e.g.SystemDefault) DEFAULT_QUALITY (0)
以下は、GDI +(graphics.DrawString)とGDI(TextRenderer.DrawText)テキストのレンダリング)の比較です。
GDI +:TextRenderingHintClearTypeGridFit
、[〜#〜] gdi [〜#〜]:CLEARTYPE_QUALITY
:
GDI +:TextRenderingHintAntiAlias
、[〜#〜] gdi [〜#〜]:ANTIALIASED_QUALITY
:
GDI +:TextRenderingHintAntiAliasGridFit
、[〜#〜] gdi [〜#〜]:サポートされていません、ANTIALIASED_QUALITYを使用します:
GDI +:TextRenderingHintSingleBitPerPixelGridFit
、[〜#〜] gdi [〜#〜]:PROOF_QUALITY
:
GDI +:TextRenderingHintSingleBitPerPixel
、[〜#〜] gdi [〜#〜]:DRAFT_QUALITY
:
DRAFT_QUALITY
がPROOF_QUALITY
と同一であるのは奇妙です。これはCLEARTYPE_QUALITY
と同一です。
も参照してください
サイズ= 11のフォント「Courier New」を作成すると、上の画像のような出力が得られます。高さが下線を含まない14ピクセルであることがわかります。幅は正確に14ピクセルです(各文字に7ピクセル)。
したがって、このフォントは14x14ピクセルをレンダリングします。
ただし、TextRenderer.MeasureText()
は代わりに21ピクセルの幅を返します。正確な値が必要な場合、これは役に立ちません。
解決策は次のコードです。
Font i_Courier = new Font("Courier New", 11, GraphicsUnit.Pixel);
Win32.SIZE k_Size;
using (Bitmap i_Bmp = new Bitmap(200, 200, PixelFormat.Format24bppRgb))
{
using (Graphics i_Graph = Graphics.FromImage(i_Bmp))
{
IntPtr h_DC = i_Graph.GetHdc();
IntPtr h_OldFont = Win32.SelectObject(h_DC, i_Courier.ToHfont());
Win32.GetTextExtentPoint32(h_DC, "Áp", 2, out k_Size);
Win32.SelectObject(h_DC, h_OldFont);
i_Graph.ReleaseHdc();
}
}
k_Sizeには正しいサイズが含まれます:14x14
重要:このコードは、通常のフォントを正しく測定します。イタリックフォント(常に右側にオーバーハングがある)にも正確な値が必要な場合は、この記事に記載されているリンクをお読みください: http://www.codeproject.com/Articles/14915/イタリックフォントのテキストの幅
付録:C#でAPI呼び出しを使用したことがない人のために、Win32クラスの作成方法のヒントを示します。これは完全ではありません。詳細は http://www.pinvoke.net をご覧ください。
using System.Runtime.InteropServices;
public class Win32
{
[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
}
[DllImport("Gdi32.dll")]
public static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);
[DllImport("Gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
}