我进行了很多搜索并尝试了很多,但我找不到合适的解决方案。
我想知道是否有任何方法可以确定指定字体的确切字形高度?
我的意思是当我想确定DOT字形的高度时,我应该收到小的高度,但不是带有填充或字体大小的高度。
我在这里找到了确定精确字形宽度的解决方案(我使用了第二种方法),但它不适用于高度。
更新:我需要 .NET 1.1 的解决方案
获得角色指标并不难。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
)。
注意:这是临时代码,在现实世界中,您应该使用ReleaseHandle
和清理 HDC 和对象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;
}
}
这是一个涉及 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();
您能否更新问题以包括您尝试过的内容?
通过点字形,我假设您的意思是此处详述的标点符号?
这个字形高度是显示在屏幕上还是打印页面上?
我设法修改了您发布的链接中的第一种方法,以便计算匹配的垂直像素,但是除非您愿意逐个字符地绘制,否则识别字形的最大高度是很繁琐的,所以这并不是真正的像文章一样的一般工作解决方案。
为了获得通用的工作解决方案,需要识别字符/字形的最大单像素垂直区域,然后计算该区域中的像素数。
我还设法验证了Graphics.MeasureString
,TextRenderer.MeasureText
并且Graphics.MeasureCharacterRanges
都返回了边界框,它给出了一个类似于字体高度的数字。
对此的替代方法是Glyph.ActualHeight
获取框架元素的渲染高度的属性。WPF 的这一部分以及相关的GlyphTypeface
和GlyphRun
类。我目前无法测试它们,只有 Mono。
获取步骤Glyph.ActualHeight
如下
初始化参数GlyphRun
初始化GlyphRun
对象
Glyph
使用glyphTypeface.CharacterToGlyphMap[text[n]]
或更正确地访问相关glyphTypeface.GlyphIndices[n]
, glyphTypeface
您的 GlyphTypeface 在哪里,它是从您在步骤 1 中创建的 Typeface 对象创建的。
使用它们的相关资源包括
关于 GDI 的进一步参考(这些类在后台使用的是 GDI 或 GDI+)和 Windows 中的字体包括