我搜索了很多并尝试了很多,但我找不到合适的解决方案。
我想知道是否有一种方法可以确定指定字体中精确字形高度?
我的意思是,当我想要确定DOT字形的高度时,我应该得到小的高度,而不是带有填充或字体大小的高度。
我已经找到了确定精确字形宽度的解决方案(在这里)(我使用了第二种方法),但它对于高度不起作用。
更新: 我需要.NET 1.1的解决方案。
我搜索了很多并尝试了很多,但我找不到合适的解决方案。
我想知道是否有一种方法可以确定指定字体中精确字形高度?
我的意思是,当我想要确定DOT字形的高度时,我应该得到小的高度,而不是带有填充或字体大小的高度。
我已经找到了确定精确字形宽度的解决方案(在这里)(我使用了第二种方法),但它对于高度不起作用。
更新: 我需要.NET 1.1的解决方案。
GetGlyphOutline
,您可以使用 GGO_METRICS
常量调用该函数,以获取在呈现时包含字形的最小外接矩形的高度和宽度。例如,Arial 字体中的点的 10 点字形将得到一个 1x1 像素的矩形,而对于字母 I,如果字体大小为 100 点,则为 95x.14。// 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);
GLYPHMETRICS
获取宽度)。ReleaseHandle
和DeleteObject
清理HDC和对象。感谢用户2173353的评论指出这一点。// 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
对象周围使用using{...}
应该可以处理这个问题。如果不是这样的话,你是对的,在using
块的结尾处,应该调用ReleaseHdc
和DeleteObject
。嗯,再想想这个:我认为最好使用SafeHandle
衍生类,而不是IntPtr
,这将自动处理清理工作。我添加了一个警告注释到这篇文章中来提醒大家注意这一点。 - Abel以下是涉及 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
对象glyphTypeface.CharacterToGlyphMap[text[n]]
或更正确的是glyphTypeface.GlyphIndices[n]
访问相关的Glyph
,其中glyphTypeface
是你创建的GlyphTypeface,它是从Step 1中创建的Typeface对象。关于GDI(这些类在后台使用的是GDI或GDI+)和Windows中的字体,进一步参考资料包括: