1

TextRenderer クラスを使用して、特定のフォントの文字列のサイズを測定しようとしています。3つの異なるアプローチ(Graphics.MeasureCharacterRanges、Graphics.MeasureString、TextRenderer.MeasureText)で測定しようとしたにもかかわらず、それらはすべて正確ではなく異なる結果をもたらしましたが、他の何かに出くわしました.
フォントサイズ 7 と 8 を使用して同じフォントで同じ文字列を測定するとSTART、フォントサイズ 7 の測定値は、フォントサイズ 8 の測定値よりも広いことがわかります。

私が使用するコードは次のとおりです。

Font f1 = new Font("Arial", 7, FontStyle.Regular);
Font f2 = new Font("Arial", 8, FontStyle.Regular);
Size s1 = TextRenderer.MeasureText("START", f1);
Size s2 = TextRenderer.MeasureText("START", f2);

その結果s1、awidthは 41 で aheightは 13 になりs2、awidthは 40 で aheightは 14 になります。

フォントを小さくすると幅が大きくなるのはなぜですか?

4

2 に答える 2

3

大きいフォントが小さい幅を生成する可能性がある理由を具体的に説明するために、このサンプル コンソール アプリをまとめました。7 と 8 のフォント サイズをそれぞれ 7.5 と 8.25 に調整したことは注目に値します。これはTextRenderer、内部的にそれらを評価するサイズです。

using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;

namespace FontSizeDifference
{
    static class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        struct ABCFLOAT
        {
            public float abcfA;
            public float abcfB;
            public float abcfC;
        }

        [DllImport("gdi32.dll")]
        static extern bool GetCharABCWidthsFloat(IntPtr hdc, int iFirstChar, int iLastChar, [Out] ABCFLOAT[] lpABCF);

        [DllImport("gdi32.dll", CharSet = CharSet.Auto, EntryPoint = "SelectObject", SetLastError = true)]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);

        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        static extern bool DeleteObject([In] IntPtr hObject);

        [StructLayout(LayoutKind.Sequential)]
        struct KERNINGPAIR
        {
            public ushort wFirst;
            public ushort wSecond;
            public int iKernAmount;
        }

        [DllImport("gdi32.dll")]
        static extern int GetKerningPairs(IntPtr hdc, int nNumPairs, [Out] KERNINGPAIR[] lpkrnpair);

        [STAThread]
        static void Main()
        {
            var fonts = new[] {
                new Font("Arial", 7.5f, FontStyle.Regular),
                new Font("Arial", 8.25f, FontStyle.Regular)
            };
            string textToMeasure = "START";

            using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
            {
                IntPtr hDC = g.GetHdc();

                foreach (Font font in fonts)
                {
                    float totalWidth = 0F;
                    IntPtr hFont = font.ToHfont();

                    // Apply the font to dc
                    SelectObject(hDC, hFont);

                    int pairCount = GetKerningPairs(hDC, short.MaxValue, null);
                    var lpkrnpair = new KERNINGPAIR[pairCount];
                    GetKerningPairs(hDC, pairCount, lpkrnpair);

                    Console.WriteLine("\r\n" + font.ToString());

                    for (int ubound = textToMeasure.Length - 1, i = 0; i <= ubound; ++i)
                    {
                        char c = textToMeasure[i];
                        ABCFLOAT characterWidths = GetCharacterWidths(hDC, c);
                        float charWidth = (characterWidths.abcfA + characterWidths.abcfB + characterWidths.abcfC);
                        totalWidth += charWidth;

                        int kerning = 0;
                        if (i < ubound)
                        {
                            kerning = GetKerningBetweenCharacters(lpkrnpair, c, textToMeasure[i + 1]).iKernAmount;
                            totalWidth += kerning;
                        }

                        Console.WriteLine(c + ": " + (charWidth + kerning) + " (" + charWidth + " + " + kerning + ")");
                    }

                    Console.WriteLine("Total width: " + totalWidth);

                    DeleteObject(hFont);
                }

                g.ReleaseHdc(hDC);
            }
        }

        static KERNINGPAIR GetKerningBetweenCharacters(KERNINGPAIR[] lpkrnpair, char first, char second)
        {
            return lpkrnpair.Where(x => (x.wFirst == first) && (x.wSecond == second)).FirstOrDefault();
        }

        static ABCFLOAT GetCharacterWidths(IntPtr hDC, char character)
        {
            ABCFLOAT[] values = new ABCFLOAT[1];
            GetCharABCWidthsFloat(hDC, character, character, values);
            return values[0];
        }
    }
}

フォント サイズごとに、カーニングを含む各文字の幅を出力します。私の場合、96 DPI では次のようになります。

[フォント: Name=Arial、Size=7.5、Units=3、GdiCharSet=1、GdiVerticalFont=False]
S: 7 (7 + 0)
T: 6 (7 + -1)
A: 7 (7 + 0)
R: 7(7+0)
T:7(7+0)
全幅:34

[フォント: Name=Arial、Size=8.25、Units=3、GdiCharSet=1、GdiVerticalFont=False]
S: 7 (7 + 0)
T: 5 (6 + -1)
A: 8 (8 + 0)
R: 7 (7 + 0)
T: 6 (6 + 0)
全幅: 33

によって行われた測定の正確な式を明らかに把握していませんがTextRenderer、同じ幅の不一致を示しています。フォント サイズ 7 では、すべての文字の幅が 7 になります。ただし、フォント サイズ 8 では、文字の幅が変化し始め、大きいものも小さいものもあり、最終的には幅が狭くなります。

于 2016-04-16T03:36:07.720 に答える
1

正しい値を与えているように見えTextRenderer.MeasureTextますが、フォントが小さいほど、一部のグリフの文字間隔が大きくなります。

以下に、テキストがどのように見えるかを示し"TTTTTTTTT"ます。上がArial 7、下がArial 8です。

大きいフォントの場合、文字間にスペースはありません。

ここに画像の説明を入力

于 2016-04-15T14:01:24.907 に答える