我把你的问题分成三部分:
-
动态字体大小,而不是硬编码字体大小
-
-
图示符应与左侧对齐
测量文本大小后,计算字体需要放大或缩小以匹配图像高度的系数:
SizeF size = TextMeasurer.Measure(text, new RendererOptions(font));
float scalingFactor = finalImage.Height / size.Height;
var scaledFont = new Font(font, scalingFactor * font.Size);
膨胀文本以使用图像的整个高度
根据每个glyph,我们现在可能在图像的顶/底面和文本的顶/底面之间有一个间隙。如何呈现或绘制glyph在很大程度上取决于所使用的字体。我不是排版方面的专家,但是每种字体都有自己的边距/填充,并且在字体周围有自己的高度
baseline
.
为了使字形与图像的顶部和底部对齐,我们必须进一步放大字体。为了计算这个系数,我们可以通过搜索高度来确定当前绘制文本的上边缘和下边缘(
y
int top = GetTopPixel(initialImage, Rgba32.White);
int bottom = GetBottomPixel(initialImage, Rgba32.White);
int offset = top + (initialImage.Height - bottom);
SizeF inflatedSize = TextMeasurer.Measure(text, new RendererOptions(scaledFont));
float inflatingFactor = (inflatedSize.Height + offset) / inflatedSize.Height;
var inflatedFont = new Font(font, inflatingFactor * scaledFont.Size);
location.Offset(0.0f, -top);
将字形移到最左边
最后,根据图示符的不同,图示符的左侧可能不会与图像的左侧对齐。与上一步类似,我们可以确定包含膨胀图示符的当前图像中文本的最左侧像素,并将文本相应地向左移动以消除两者之间的间隙:
int left = GetLeftPixel(intermediateImage, Rgba32.White);
location.Offset(-left, 0.0f);
现在我们可以绘制与图像左侧对齐的文本:
现在,最终图像的字体根据图像的大小进行动态缩放,并进一步放大和移动以填充图像的整个高度,并且进一步移动以使左侧没有间隙。
注意
的
TextGraphicsOptions
DPI公司
图片内容:
var textGraphicOptions = new TextGraphicsOptions(true)
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
DpiX = (float)finalImage.MetaData.HorizontalResolution,
DpiY = (float)finalImage.MetaData.VerticalResolution
};
代码
private static void CreateImageFiles()
{
Directory.CreateDirectory("output");
string text = "J";
Rgba32 backgroundColor = Rgba32.White;
Rgba32 foregroundColor = Rgba32.Black;
int imageWidth = 256;
int imageHeight = 256;
using (var finalImage = new Image<Rgba32>(imageWidth, imageHeight))
{
finalImage.Mutate(context => context.Fill(backgroundColor));
finalImage.MetaData.HorizontalResolution = 96;
finalImage.MetaData.VerticalResolution = 96;
FontFamily fontFamily = SystemFonts.Find("Arial");
var font = new Font(fontFamily, 10, FontStyle.Regular);
var textGraphicOptions = new TextGraphicsOptions(true)
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
DpiX = (float)finalImage.MetaData.HorizontalResolution,
DpiY = (float)finalImage.MetaData.VerticalResolution
};
SizeF size = TextMeasurer.Measure(text, new RendererOptions(font));
float scalingFactor = finalImage.Height / size.Height;
var scaledFont = new Font(font, scalingFactor * font.Size);
PointF location = new PointF();
using (Image<Rgba32> initialImage = finalImage.Clone(context => context.DrawText(textGraphicOptions, text, scaledFont, foregroundColor, location)))
{
initialImage.Save("output/initial.png");
int top = GetTopPixel(initialImage, backgroundColor);
int bottom = GetBottomPixel(initialImage, backgroundColor);
int offset = top + (initialImage.Height - bottom);
SizeF inflatedSize = TextMeasurer.Measure(text, new RendererOptions(scaledFont));
float inflatingFactor = (inflatedSize.Height + offset) / inflatedSize.Height;
var inflatedFont = new Font(font, inflatingFactor * scaledFont.Size);
location.Offset(0.0f, -top);
using (Image<Rgba32> intermediateImage = finalImage.Clone(context => context.DrawText(textGraphicOptions, text, inflatedFont, foregroundColor, location)))
{
intermediateImage.Save("output/intermediate.png");
int left = GetLeftPixel(intermediateImage, backgroundColor);
location.Offset(-left, 0.0f);
finalImage.Mutate(context => context.DrawText(textGraphicOptions, text, inflatedFont, foregroundColor, location));
finalImage.Save("output/final.png");
}
}
}
}
private static int GetTopPixel(Image<Rgba32> image, Rgba32 backgroundColor)
{
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
Rgba32 pixel = image[x, y];
if (pixel != backgroundColor)
{
return y;
}
}
}
throw new InvalidOperationException("Top pixel not found.");
}
private static int GetBottomPixel(Image<Rgba32> image, Rgba32 backgroundColor)
{
for (int y = image.Height - 1; y >= 0; y--)
{
for (int x = image.Width - 1; x >= 0; x--)
{
Rgba32 pixel = image[x, y];
if (pixel != backgroundColor)
{
return y;
}
}
}
throw new InvalidOperationException("Bottom pixel not found.");
}
private static int GetLeftPixel(Image<Rgba32> image, Rgba32 backgroundColor)
{
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
Rgba32 pixel = image[x, y];
if (pixel != backgroundColor)
{
return x;
}
}
}
throw new InvalidOperationException("Left pixel not found.");
}
我们不需要保存所有的3个图像,但是我们需要创建所有的3个图像,并逐步膨胀和移动文本,以便填充图像的整个高度,并从图像的最左侧开始。
此解决方案独立于使用的字体工作。另外,对于生产应用程序,请避免通过
SystemFonts
,因为有问题的字体可能在目标计算机上不可用。要获得稳定的独立解决方案,请部署
FontCollection
手动。