11

私はたくさん検索してたくさん試しましたが、適切な解決策を見つけることができません。

指定されたフォントで正確なグリフの高さを決定する方法はありますか?

ここで、 DOTグリフの高さを決定する場合は、低い高さを受け取る必要がありますが、パディングやフォントサイズを使用した高さは受け取りません。

ここで正確なグリフ幅を決定するための解決策を見つけました(2番目のアプローチを使用しました)が、高さでは機能しません。

更新:.NET1.1のソリューションが必要です

4

3 に答える 3

7

文字メトリックを取得するのはそれほど難しくありません。GDIにはGetGlyphOutline、定数を使用して呼び出すことができる関数が含まれており、レンダリング時にグリフを含めるために必要な最小の囲み長方形GGO_METRICSの高さと幅を取得できます。つまり、フォントArialのドットの10ポイントのグリフは、1x1ピクセルの長方形になり、フォントのサイズが100ポイントの場合、文字Iの場合は95x.14になります。

これらは、P/Invoke呼び出しの宣言です。

// the declarations
public struct FIXED
{
    public short fract;
    public short value;
}

public struct MAT2
{
    [MarshalAs(UnmanagedType.Struct)] public FIXED eM11;
    [MarshalAs(UnmanagedType.Struct)] public FIXED eM12;
    [MarshalAs(UnmanagedType.Struct)] public FIXED eM21;
    [MarshalAs(UnmanagedType.Struct)] public FIXED eM22;
}

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public int x;
    public int y;
}

[StructLayout(LayoutKind.Sequential)]
public struct POINTFX
{
    [MarshalAs(UnmanagedType.Struct)] public FIXED x;
    [MarshalAs(UnmanagedType.Struct)] public FIXED y;
}

[StructLayout(LayoutKind.Sequential)]
public struct GLYPHMETRICS
{

    public int gmBlackBoxX;
    public int gmBlackBoxY;
    [MarshalAs(UnmanagedType.Struct)] public POINT gmptGlyphOrigin;
    [MarshalAs(UnmanagedType.Struct)] public POINTFX gmptfxGlyphOrigin;
    public short gmCellIncX;
    public short gmCellIncY;

}

private const int GGO_METRICS = 0;
private const uint GDI_ERROR = 0xFFFFFFFF;

[DllImport("gdi32.dll")]
static extern uint GetGlyphOutline(IntPtr hdc, uint uChar, uint uFormat,
   out GLYPHMETRICS lpgm, uint cbBuffer, IntPtr lpvBuffer, ref MAT2 lpmat2);

[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

P / Invokeの冗長性を考慮しない場合、実際のコードはかなり些細なものです。私はコードをテストしました、それは動作します(幅を取得するために調整することもできますGLYPHMETRICS)。

注:これはアドホックコードです。現実の世界では、HDCとオブジェクトをReleaseHandleとでクリーンアップする必要がありますDeleteObject。これを指摘するためのuser2173353によるコメントに感謝します。

// if you want exact metrics, use a high font size and divide the result
// otherwise, the resulting rectangle is rounded to nearest int
private int GetGlyphHeight(char letter, string fontName, float fontPointSize)
{
    // init the font. Probably better to do this outside this function for performance
    Font font = new Font(new FontFamily(fontName), fontPointSize);
    GLYPHMETRICS metrics;

    // identity matrix, required
    MAT2 matrix = new MAT2
        {
            eM11 = {value = 1}, 
            eM12 = {value = 0}, 
            eM21 = {value = 0}, 
            eM22 = {value = 1}
        };

    // HDC needed, we use a bitmap
    using(Bitmap b = new Bitmap(1,1))
    using (Graphics g = Graphics.FromImage(b))
    {
        IntPtr hdc = g.GetHdc();
        IntPtr prev = SelectObject(hdc, font.ToHfont());
        uint retVal =  GetGlyphOutline(
             /* handle to DC   */ hdc, 
             /* the char/glyph */ letter, 
             /* format param   */ GGO_METRICS, 
             /* glyph-metrics  */ out metrics, 
             /* buffer, ignore */ 0, 
             /* buffer, ignore */ IntPtr.Zero, 
             /* trans-matrix   */ ref matrix);

        if(retVal == GDI_ERROR)
        {
            // something went wrong. Raise your own error here, 
            // or just silently ignore
            return 0;
        }


        // return the height of the smallest rectangle containing the glyph
        return metrics.gmBlackBoxY;
    }    
}
于 2012-04-07T18:43:15.580 に答える
2

質問を更新して、試したことを含めることができますか?

ドットグリフとは、ここで詳しく説明されている句読点を意味していると思いますか?

このグリフの高さは画面に表示されますか、それとも印刷されたページに表示されますか?

一致する垂直ピクセルをカウントするために、投稿したリンクの最初の方法を変更することができましたが、文字ごとに描画する意思がない限り、グリフの最大の高さを特定するのは面倒なので、実際にはそうではありませんでした記事のような一般的な実用的なソリューション。

一般的な解決策を得るには、文字/グリフの最大の単一ピクセルの垂直領域を特定し、その領域のピクセル数を数える必要があります。

また、それを確認することができGraphics.MeasureString、すべてがフォントの高さに似た数値を与えるバウンディングボックスを返しました。TextRenderer.MeasureTextGraphics.MeasureCharacterRanges

これに代わる方法Glyph.ActualHeightは、フレームワーク要素のレンダリングされた高さを取得するプロパティです。WPFのこの部分と、関連するGlyphTypefaceクラスGlyphRun。現時点では、Monoだけでテストすることはできませんでした。

取得の手順はGlyph.ActualHeight次のとおりです

  1. の引数を初期化しますGlyphRun

  2. GlyphRunオブジェクトを初期化します

  3. ステップ1で作成したTypefaceオブジェクトから作成されたGlyphTypefaceGlyphを使用してglyphTypeface.CharacterToGlyphMap[text[n]]、またはより正確glyphTypeface.GlyphIndices[n]に 関連するアクセスを行います。glyphTypeface

それらの使用に関連するリソースには、次のものがあります。

GDI(これらのクラスが内部で使用するのはGDIまたはGDI +)およびWindowsのフォントに関するその他のリファレンスには次のものがあります。

于 2012-04-01T14:28:21.840 に答える
2

これがWPFを含むソリューションです。テキストの正確なバウンディングボックスを取得するために、中間のGeometryオブジェクトを作成します。このソリューションの利点は、実際には何もレンダリングされないことです。インターフェイスにWPFを使用しない場合でも、フォントのレンダリングサイズがGDIで同じか、十分に近いと仮定して、このコードを使用して測定のみを行うことができます。

    var fontFamily = new FontFamily("Arial");
    var typeface = new Typeface(fontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
    var fontSize = 15;

    var formattedText = new FormattedText(
        "Hello World",
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        typeface,
        fontSize,
        Brushes.Black);

    var textGeometry = formattedText.BuildGeometry(new Point(0, 0));

    double x = textGeometry.Bounds.Left;
    double y = textGeometry.Bounds.Right;
    double width = textGeometry.Bounds.Width;
    double height = textGeometry.Bounds.Height;

ここで、「Helloworld」の測定値は約77x11単位です。1つのドットで1.5x1.5になります。

別の解決策として、まだWPFで、GlyphRunとComputeInkBoundingBox()を使用できます。ただし、これはもう少し複雑で、自動フォント置換をサポートしません。次のようになります。

var glyphRun = new GlyphRun(glyphTypeFace, 0, false, fontSize,
            glyphIndexList,
            new Point(0, 0),
            advanceWidths,
            null, null, null, null,
            null, null);

Rect glyphInkBox = glyphRun.ComputeInkBoundingBox();
于 2012-04-03T13:03:42.753 に答える