C# 使用抽绳将文本顶部对齐
我想使用C# 使用抽绳将文本顶部对齐,c#,drawstring,C#,Drawstring,我想使用抽绳垂直对齐不同大小字体的文本。基本上,我试图在一份报告上打印一个卖出价,用比美元小的字体打印美分。这是在报告引擎中,我需要写入图像,而不是Windows窗体,因此我相信我必须使用DrawString而不是textrender 我发现了各种各样的文章,解释了如何在基线上绘制文本。虽然我发现同一字体在不同的大小下可以显示一个像素,但它们似乎工作正常。我想我可以拿着这些来解决如何使用字体的ascent设计信息对齐,但我没有成功:-( 有人有一些示例代码可以帮助您吗 更新1:更改总数以反映我希
抽绳
垂直对齐不同大小字体的文本。基本上,我试图在一份报告上打印一个卖出价,用比美元小的字体打印美分。这是在报告引擎中,我需要写入图像,而不是Windows窗体,因此我相信我必须使用DrawString
而不是textrender
我发现了各种各样的文章,解释了如何在基线上绘制文本。虽然我发现同一字体在不同的大小下可以显示一个像素,但它们似乎工作正常。我想我可以拿着这些来解决如何使用字体的ascent设计信息对齐,但我没有成功:-(
有人有一些示例代码可以帮助您吗
更新1:更改总数以反映我希望顶部对齐而不是垂直对齐。我希望在Arial 20中打印的美元值顶部与Arial 14中的美分值顶部水平对齐。我尝试过的所有字体都有问题
在下图中,我使用的是Arial Black
您可以看到,对于较大的字体,红线和1的顶部之间有一个间隙,但当它到达较小的字体时,间隙消失了
使用Arial,它从线上方开始
我认为这是Verdana,它的字体越大,间距越大
对于这三种情况,我使用以下代码:
float offset = myFont.SizeInPoints /
myFont.FontFamily.GetEmHeight(myFont.Style) *
(myFont.FontFamily.GetLineSpacing(myFont.Style) - myFont.FontFamily.GetCellAscent(myFont.Style));
float pixels = e.Graphics.DpiY / 72f * offset;
float baseline = pixels;// (int)(pixels + 0.5f);
PointF renderPt = new PointF(left, y - baseline);
e.Graphics.DrawString("$1", myFont, new SolidBrush(Color.Black), renderPt);
我还将其用作一些测试的基础。我认为如果上升线的绘制方式是准确的,那么我可以简单地调整初始写入点。唉,当您使用更大的字体大小或不同的字体时,上升线绘制不准确,因此我无法遵循该路径
我没有使用
textrender
,因为我没有使用windows窗体OnPaint
事件,无法确定如何使用它,也无法确定如何获得适当的图形。时间有点紧迫,但我认为这可能是我的下一个选择。字体是从phy继承下来的一个领域西卡尔世界以历史为基础。不幸的是,这与计算机的工作方式并不总是一致的
例如,物理世界中的字体大小不必等于物理世界中的相同字体大小(无打字错误)。将这些字形设置为相同大小:
虽然大小相同(64点)它们的大小不相等。这与字体的历史方面有关,在现实世界中,字形被放置在方形金属板上。所指的是这些板的大小,而不是上面的字形——它们可以填满整个板;也可以不填满。基于计算机的字体也是如此。你可以看到边界标志符号的ng框相同(=字体大小),但标志符号本身不同
这通常不是排版或印刷的问题,因为人们可以快速进行调整以弥补这一点
在.net/GDI+中绘制时,在特殊情况下是不同的。基线是“始终正确的”,即:如果使用基线,则保证相同的对齐方式,但不包括字形的“底部”(不包括其下降)。当需要从顶部对齐时,将遇到问题
解决此问题的一种方法(在GDI+中)是实际扫描字形位图以查找顶部的起始位置,然后使用表示该结果的偏移量绘制字形。使用BitmapLock扫描并直接访问缓冲区
当然,您也可以使用Bitmap.GetPixel
进行扫描,但是如果使用大量文本,这将是一个非常缓慢的过程
更新:
我忘了提到一种称为方位角的东西——在本例中,上侧方位角描述了从上升顶部到字形顶部的间隙。不幸的是,您无法通过GDI/GDI+(未编写字体解析器)提取此信息。但是,WPF允许您从字形提取此信息
尽管未在本图中显示,但它显示了轮廓的不同部分,包括与顶部等效的侧轴承:
有关更多信息,请参阅此链接:也许这段代码会有所帮助。我用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();
}
字体是一个在历史基础上从物理世界继承下来的领域。不幸的是,它并不总是与计算机的工作方式兼容 例如,物理世界中的字体大小不必等于物理世界中的相同字体大小(无打字错误)。将这些字形设置为相同大小: 虽然大小相同(64点)它们的大小不相等。这与字体的历史方面有关,在现实世界中,字形被放置在方形金属板上。所指的是这些板的大小,而不是上面的字形——它们可以填满整个板;也可以不填满。基于计算机的字体也是如此。你可以看到边界标志符号的ng框相同(=字体大小),但标志符号本身不同 这通常不是排版或印刷的问题,因为人们可以快速进行调整以弥补这一点 在.net/GDI+中绘制时,在特殊情况下是不同的。基线是“始终正确的”,即:如果使用基线,则保证相同的对齐方式,但不包括字形的“底部”(不包括其下降)。当需要从顶部对齐时,将遇到问题 解决这个问题的一种方法(在GDI+中)是
//-----------------------------------------------------------------------------------------------------------------
// MeasureLeading Function
// Measures the amount of white space above a line of text, in pixels. This is accomplished by drawing the text
// onto an offscreen bitmap and then looking at each row of pixels until a non-white pixel is found.
// The y coordinate of that pixel is the result. This represents the offset by which a line of text needs to be
// raised vertically in order to make it top-justified.
//-----------------------------------------------------------------------------------------------------------------
public static int MeasureLeading(string Text, Font Font) {
Size sz = MeasureText(Text, Font);
Bitmap offscreen = new Bitmap(sz.Width, sz.Height);
Graphics ofg = Graphics.FromImage(offscreen);
ofg.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, sz.Width, sz.Height));
ofg.DrawString(Text, Font, new SolidBrush(Color.Black), 0, 0, StringFormat.GenericTypographic);
for (int iy=0; iy<sz.Height; iy++) {
for (int ix=0; ix<sz.Width; ix++) {
Color c = offscreen.GetPixel(ix, iy);
if ((c.R!=255) || (c.G!=255) || (c.B!=255)) return iy;
}
}
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
// MeasureText Method
// TextRenderer.MeasureText always adds about 1/2 em width of white space on the right,
// even when NoPadding is specified. But it returns zero for an empty string.
// To get the true string width, we measure the width of a string containing a single period
// and subtract that from the width of our original string plus a period.
//-----------------------------------------------------------------------------------------------------------------
public static System.Drawing.Size MeasureText(string Text, System.Drawing.Font Font) {
System.Windows.Forms.TextFormatFlags flags
= System.Windows.Forms.TextFormatFlags.Left
| System.Windows.Forms.TextFormatFlags.Top
| System.Windows.Forms.TextFormatFlags.NoPadding
| System.Windows.Forms.TextFormatFlags.NoPrefix;
System.Drawing.Size szProposed = new System.Drawing.Size(int.MaxValue, int.MaxValue);
System.Drawing.Size sz1 = System.Windows.Forms.TextRenderer.MeasureText(".", Font, szProposed, flags);
System.Drawing.Size sz2 = System.Windows.Forms.TextRenderer.MeasureText(Text + ".", Font, szProposed, flags);
return new System.Drawing.Size(sz2.Width - sz1.Width, sz2.Height);
}