确定指定字体的确切字形高度

时间:2012-03-29 11:02:34

标签: c# fonts .net-1.1 measurement glyph

我经常搜索并尝试了很多,但我找不到合适的解决方案。

我想知道是否有任何方法可以确定指定字体中的完全字形高度

我的意思是在这里,当我想确定 DOT 字形的高度时,我应该收到较小的高度,但不能使用填充或字体大小。

我找到了确定精确字形宽度here的解决方案(我使用过第二种方法),但它不适用于高度。

更新:我需要.NET 1.1的解决方案

3 个答案:

答案 0 :(得分: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的宽度)。

注意:这是ad-hoc代码,在现实世界中,您应该使用ReleaseHandleDeleteObject清理HDC和对象。感谢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;
    }    
}

答案 1 :(得分:2)

您可以更新问题以包含您尝试过的内容吗?

通过点字形我假设你的意思是标点符号详细here

这个字形高度是显示在屏幕还是打印页面上?

我设法修改link you posted中的第一个方法以计算匹配的垂直像素,但是除非您愿意逐个字符地绘制,否则识别字形的最大高度是很繁琐的,所以这并不像文章那样真的是一般的工作解决方案。

为了获得一般的工作解决方案,需要识别字符/字形的最大单个像素垂直区域,然后计算该区域中的像素数。

我还设法确认Graphics.MeasureStringTextRenderer.MeasureTextGraphics.MeasureCharacterRanges都返回了包含类似于字体高度的数字的边界框。

替代方法是Glyph.ActualHeight属性获取框架元素的渲染高度。这部分WPF以及相关的GlyphTypefaceGlyphRun类。我此时只能用Mono测试它们。

获取Glyph.ActualHeight的步骤如下

  1. 初始化GlyphRun

  2. 的参数
  3. 初始化GlyphRun对象

  4. 使用Glyph或更正确glyphTypeface.CharacterToGlyphMap[text[n]]访问相关的glyphTypeface.GlyphIndices[n],其中glyphTypeface是您的GlyphTypeface,它是根据您在步骤1中创建的字体对象创建的

  5. 使用它们的相关资源包括

    关于GDI的更多参考(这些类在幕后使用的是GDI或GDI +)和Windows中的字体包括

答案 2 :(得分: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;

此处,“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();