簡體   English   中英

C#Textrenderer - 測量較小的字體大小會導致更大的尺寸

[英]C# Textrenderer - Measuring smaller fontsize results in larger size

我試圖使用TextRenderer類測量給定某種字體的字符串的大小。 盡管我嘗試使用3種不同的方法(Graphics.MeasureCharacterRanges,Graphics.MeasureString,TextRenderer.MeasureText)測量它,但它們都給了我不同的結果而不准確,我偶然發現了其他東西。
使用7和8的字體大小以相同的字體測量相同的字符串START ,fontsize 7測量結果比fontsize 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);

結果是s1width為41, height為13,而s2width為40, height為14。

為什么較小的字體會產生較大的寬度?

為了明確解釋為什么更大的字體可以產生更小的寬度,我將這個示例控制台應用程序放在一起。 值得注意的是,我將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,對我來說,這導致:

[字體:名稱= Arial,大小= 7.5,單位= 3,GdiCharSet = 1,GdiVerticalFont = False]
S:7(7 + 0)
T:6(7 + -1)
答:7(7 + 0)
R:7(7 + 0)
T:7(7 + 0)
總寬度:34

[字體:名稱= Arial,大小= 8.25,單位= 3,GdiCharSet = 1,GdiVerticalFont = False]
S:7(7 + 0)
T:5(6 + -1)
答:8(8 + 0)
R:7(7 + 0)
T:6(6 + 0)
總寬度:33

雖然我顯然沒有捕獲TextRenderer所做測量的確切公式,但它確實說明了相同的寬度差異。 在字體大小為7時,所有字符的寬度均為7。 但是,在字體大小為8時,字符寬度開始變化,一些較大,一些較小,最終加起來較小的寬度。

似乎TextRenderer.MeasureText提供了正確的值,但是較小的字體對於某些字形具有更大的字母間距。

您可以在下面看到它如何查找"TTTTTTTTT"文本。 上面一個是Arial 7 ,下面一個是Arial 8

對於較大的字體,字母之間沒有空格。

在此輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM