Определение точной высоты глифа в указанном шрифте

Я много искал и много пробовал, но я не могу найти правильное решение.

интересно, есть ли какой-либо подход для определения точно символ высота указанным шрифтом?

Я имею в виду, когда я хочу определить высоту точка глиф я должен получить небольшую высоту, но не высоту с прокладками или размером шрифта.

Я нашел решение для определения точно ширина глиф здесь (я использовал второй подход), но он не работает для высоты.

обновление: мне нужно решение для .NET 1.1

3 ответов


это не это трудно получить метрики характер. GDI содержит функцию GetGlyphOutline что вы можете позвонить с GGO_METRICS постоянный, чтобы получить высоту и ширину маленький прямоугольник должен содержать символ при отображении. Т. е. 10-точечный глиф для точки в шрифте Arial даст прямоугольник 1x1 пикселей, а для буквы I 95x.14 если размер шрифта составляет 100 пунктов.

это декларация для П/вызовы:

// 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;
    }    
}

вы можете обновить вопрос, чтобы включить то, что вы пробовали ?

точка глиф я предполагаю, что вы имеете в виду знак препинания полная здесь ?

эта высота глифа отображается на экране или печатной странице ?

мне удалось изменить первый метод в ссылку ты выложил для подсчета совпадающих вертикальных пикселей, однако определение наибольшей высоты глифа необходимо делать, если вы не хотите нарисуйте символ за символом, так что это не было действительно общим рабочим решением, как статья.

для того, чтобы иметь общее рабочее решение, необходимо определить самую большую вертикальную область одного пикселя символа / глифа, а затем подсчитать количество пикселей в этой области.

мне также удалось проверить это Graphics.MeasureString, TextRenderer.MeasureText и Graphics.MeasureCharacterRanges все вернули ограничивающую рамку, которая дала число, подобное высоте шрифта.

альтернатива этому это Glyph.ActualHeight свойство, которое получает отображаемую высоту элемента framework. Эта часть WPF и связанная с ней GlyphTypeface и GlyphRun классы. Я не смог проверить их в это время, имея только моно.

шаги для получения Glyph.ActualHeight следующим образом

  1. инициализировать аргументы для GlyphRun

  2. инициализации GlyphRun объект

  3. доступ к соответствующим Glyph используя glyphTypeface.CharacterToGlyphMap[text[n]] или больше правильно glyphTypeface.GlyphIndices[n], где glyphTypeface ваш GlyphTypeface, который создается из объекта шрифта в шаге 1.

соответствующие ресурсы по их использованию включают

Futher ссылки на GDI (что эти классы используют под капотом GDI или GDI+) и шрифты в Windows включают


вот решение с участием WPF. Мы создаем промежуточный объект геометрии, чтобы получить точную ограничивающую рамку нашего текста. Преимущество этого решения заключается в том, что оно фактически ничего не отображает. Даже если вы не используете 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;

здесь измерения "Hello world"составляют около 77 x 11 единиц. Сингл точка дает 1.5 x 1.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();