字体是一个继承了许多具有历史基础的物理世界的领域。不幸的是,这并不总是与计算机的工作方式兼容。
例如,物理世界中的字体大小不必等于物理世界中的相同字体大小(没有错字)。以相同的大小获取这些字形:
尽管它们的大小相同(64 磅),但它们的大小并不相等。这与字体的历史方面有关,在物理世界中,字形放置在方形金属板上。指的是这些盘子的大小,而不是上面的字形——它们可以填满整个盘子;或不。基于计算机的字体也是如此。您可以看到字形的边界框是相同的(=字体大小),但它们自身的字形不同。
这通常不是排版或印刷的问题,因为可以快速进行调整以弥补这一点。
在 .net/GDI+ 中绘图,在特殊情况下是另一回事。基线“始终正确”,即:如果您使用基线,则可以保证相同的对齐方式,但在字形的“底部”(不包括其下降)。当您需要从顶部对齐它时,您会遇到问题。
解决这个问题的一种方法(在 GDI+ 中)是实际扫描字形位图以查找顶部的开头,然后使用表示该结果的偏移量绘制字形。使用 BitmapLock 扫描并直接访问缓冲区。
当然,您也可以使用它Bitmap.GetPixel
来进行扫描,但是对于大量文本来说,这将是一个非常缓慢的过程。
更新:
我忘了提到一种叫做轴承的东西——在这种情况下,顶部轴承描述了从上升顶部到字形顶部的间隙。不幸的是,您无法通过 GDI/GDI+ 提取它(无需编写字体解析器)。然而,WPF 允许您从字形中提取此信息。
尽管此图中未显示,但它显示了字形的不同部分,包括。与顶部等效的侧轴承:
有关详细信息,请参阅此链接:http:
//msdn.microsoft.com/en-us/library/system.windows.media.glyphtypeface.aspx
也许这段代码可以提供帮助。我在 VB 中编写了这个并翻译成 C#(所以我希望没有在翻译中丢失)。这将获取一个字形并返回它的位图,并为字形本身提供一个精确的边界框。这种方式只需将生成的位图放在您需要的垂直位置:
它需要 WPF 字体作为参数(使用 WPF 而不是 GDI+ 打开的字体) - 如果您需要帮助,请告诉我:
using System.Windows.Media;
using System.Windows.Media.Imaging;
static class WPFGlyphToGDIPBitmap
{
public static System.Drawing.Bitmap GetBitmapOfChar(GlyphTypeface gt, _
char c, _
double ptSize, _
float dpi)
{
ushort ci = 0;
if (!gt.CharacterToGlyphMap.TryGetValue(Strings.AscW(c), ci)) {
if (!gt.CharacterToGlyphMap.TryGetValue(Strings.Asc(c), ci))
return null;
}
Geometry geo = gt.GetGlyphOutline(ci, ptSize, ptSize);
GeometryDrawing gDrawing = new GeometryDrawing(System.Windows.Media.Brushes.Black, null, geo);
DrawingImage geoImage = new DrawingImage(gDrawing);
geoImage.Freeze();
DrawingVisual viz = new DrawingVisual();
DrawingContext dc = viz.RenderOpen;
dc.DrawImage(geoImage, new Rect(0, 0, geoImage.Width, geoImage.Height));
dc.Close();
RenderTargetBitmap bmp = new RenderTargetBitmap(geoImage.Width, _
geoImage.Height, _
dpi, dpi, _
PixelFormats.Pbgra32);
bmp.Render(viz);
PngBitmapEncoder enc = new PngBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bmp));
MemoryStream ms = new MemoryStream();
enc.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
enc = null;
dc = null;
viz = null;
DisposeBitmap(bmp);
System.Drawing.Bitmap gdiBMP = new System.Drawing.Bitmap(ms);
ms.Dispose();
//gdiBMP.Save("c:\test.png", System.Drawing.Imaging.ImageFormat.Png)
return gdiBMP;
}
}
public static void DisposeBitmap(RenderTargetBitmap bmp)
{
if (bmp != null) {
bmp.Clear();
}
bmp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}