Определение точной высоты глифа в указанном шрифте - PullRequest
11 голосов
/ 29 марта 2012

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

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

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

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

ОБНОВЛЕНИЕ: Мне нужно решение для .NET 1.1

Ответы [ 3 ]

7 голосов
/ 07 апреля 2012

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

Это объявление для 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;
    }    
}
2 голосов
/ 03 апреля 2012

Вот решение с участием WPF.Мы создаем промежуточный объект Geometry, чтобы получить точную ограничительную рамку нашего текста.Преимущество этого решения в том, что оно фактически ничего не рендерит.Даже если вы не используете WPF для своего интерфейса, вы можете использовать этот фрагмент кода только для измерений, предполагая, что размер рендеринга шрифта будет таким же, как в GDI, или достаточно близким.Здесь размеры «Hello world» составляют 77 х 11 единиц.Одна точка дает 1,5 х 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();
2 голосов
/ 01 апреля 2012

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

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

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

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

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

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

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

Шаги для получения Glyph.ActualHeight следующие

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

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

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

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

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

...