Cra*_*aig 4 c# .net-core imagesharp
我的任务是创建要打印的图像。在图片上,我需要输入一个大写字母(大写[AZ])。
打印的图像尺寸可以在15厘米高和30厘米高之间变化(包括两者之间的任何尺寸)。
字母需要横跨打印图像的整个高度。
设置字体大小时,我看到您可以获取文本的大小。
using (Image<Rgba32> img = new Image<Rgba32>(imageWidth, imageHeight))
{
img.Mutate(x => x.Fill(Rgba32.White));
img.MetaData.HorizontalResolution = 96;
img.MetaData.VerticalResolution = 96;
var fo = SystemFonts.Find("Arial");
var font = new Font(fo, 1350, FontStyle.Regular);
Run Code Online (Sandbox Code Playgroud)
我可以在这里获取文本的大小:
SizeF size = TextMeasurer.Measure(group.Text, new RendererOptions(font));
Run Code Online (Sandbox Code Playgroud)
但是,如您所见,我在这里将字体的大小硬编码。高度需要与图像的高度匹配。
有什么方法可以指定这一点,而又不会增加质量?有没有一种方法可以指定高度(以像素为单位)?也许我可以安全使用的字体大小有颜色?
当我将“字体大小”设置为“图像”的像素高度时,会看到以下信息:

我不确定为什么圆圈部分有间隙。我将左手文本的左上角位置设置为0,0 ....,将“ QWW”组的右上角点设置为图像的宽度,将0设置为Y。但是我希望它们要与尺寸和底部齐平。
TextMeasurer 专为行和单词上下文中的度量文本而设计,而不是在单个字符上,因为它不查看单个字形形式,而是查看整个字体以针对行间距等进行测量。
相反,您需要使用 nuget 包将字形直接渲染为向量SixLabors.Shapes.Text。这将允许您准确测量最终字形 + 应用缩放和变换以确保字形与图像边缘对齐。除了最终将字形绘制到图像之外,它还可以使您不必执行任何昂贵的像素级操作。
/// <param name="text">one or more characters to scale to fill as much of the target image size as required.</param>
/// <param name="targetSize">the size in pixels to generate the image</param>
/// <param name="outputFileName">path/filename where to save the image to</param>
private static void GenerateImage(string text, Primitives.Size targetSize, string outputFileName)
{
FontFamily fam = SystemFonts.Find("Arial");
Font font = new Font(fam, 100); // size doesn't matter too much as we will be scaling shortly anyway
RendererOptions style = new RendererOptions(font, 72); // again dpi doesn't overlay matter as this code genreates a vector
// this is the important line, where we render the glyphs to a vector instead of directly to the image
// this allows further vector manipulation (scaling, translating) etc without the expensive pixel operations.
IPathCollection glyphs = SixLabors.Shapes.TextBuilder.GenerateGlyphs(text, style);
var widthScale = (targetSize.Width / glyphs.Bounds.Width);
var heightScale = (targetSize.Height / glyphs.Bounds.Height);
var minScale = Math.Min(widthScale, heightScale);
// scale so that it will fit exactly in image shape once rendered
glyphs = glyphs.Scale(minScale);
// move the vectorised glyph so that it touchs top and left edges
// could be tweeked to center horizontaly & vertically here
glyphs = glyphs.Translate(-glyphs.Bounds.Location);
using (Image<Rgba32> img = new Image<Rgba32>(targetSize.Width, targetSize.Height))
{
img.Mutate(i => i.Fill(new GraphicsOptions(true), Rgba32.Black, glyphs));
img.Save(outputFileName);
}
}
Run Code Online (Sandbox Code Playgroud)
我将您的问题分为三个部分:
在测量了文本大小之后,计算需要放大或缩小字体以匹配图像高度的因子:
SizeF size = TextMeasurer.Measure(text, new RendererOptions(font));
float scalingFactor = finalImage.Height / size.Height;
var scaledFont = new Font(font, scalingFactor * font.Size);
Run Code Online (Sandbox Code Playgroud)
这样,初始设置的字体大小在很大程度上被忽略。现在,我们可以根据图像的高度使用动态缩放的字体绘制文本:
根据每个字形,我们现在可能在图像的顶部/底部与文本的顶部/底部之间有一个缝隙。字形的渲染或绘制方式在很大程度上取决于所使用的字体。我不是排版专家,但是AFAIK每种字体都有其自己的边距/填充,并在基线附近具有自定义的高度。
为了使字形与图像的顶部和底部对齐,我们必须进一步放大字体。要计算此因子,我们可以通过搜索最顶部和最底部像素的高度(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);
Run Code Online (Sandbox Code Playgroud)
现在,我们可以绘制文本的顶部和底部,使其紧贴图像的顶部和底部边缘:
最后,取决于字形,字形的左侧可能不会与图像的左侧对齐。与上一步相似,我们可以确定当前图像中包含膨胀字形的文本的最左侧像素,然后将文本相应地向左移动以消除两者之间的间隙:
int left = GetLeftPixel(intermediateImage, Rgba32.White);
location.Offset(-left, 0.0f);
Run Code Online (Sandbox Code Playgroud)
现在我们可以绘制与图像左侧对齐的文本:
现在,此最终图像具有根据图像大小动态缩放的字体,已进一步缩放并移动以填满图像的整个高度,并且已进一步移动以使左侧没有间隙。
当绘制文本时,DPI的TextGraphicsOptions应该匹配DPI的图像:
var textGraphicOptions = new TextGraphicsOptions(true)
{
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
DpiX = (float)finalImage.MetaData.HorizontalResolution,
DpiY = (float)finalImage.MetaData.VerticalResolution
};
Run Code Online (Sandbox Code Playgroud)
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.");
}
Run Code Online (Sandbox Code Playgroud)
我们不需要保存所有3张图像,但是我们确实需要创建所有3张图像并逐步填充和移动文本,以填充图像的整个高度并从图像的最左侧开始。
该解决方案独立于所使用的字体工作。另外,对于生产应用程序,请避免通过查找字体SystemFonts,因为所讨论的字体可能在目标计算机上不可用。要获得稳定的独立解决方案,请在应用程序中部署TTF字体,然后FontCollection手动安装该字体。
| 归档时间: |
|
| 查看次数: |
643 次 |
| 最近记录: |