Я разделил ваш вопрос на 3 части:
- динамический размер шрифта, а не жестко заданный размер шрифта
- глиф должен использовать полную высоту изображения
- глиф должен быть выровнен по левому краю
Динамическое масштабирование текста для заполнения высоты изображения
После измерения размера текста рассчитайте коэффициент, по которому шрифтнеобходимо увеличить или уменьшить масштаб, чтобы соответствовать высоте изображения:
SizeF size = TextMeasurer.Measure(text, new RendererOptions(font));
float scalingFactor = finalImage.Height / size.Height;
var scaledFont = new Font(font, scalingFactor * font.Size);
Таким образом, изначально установленный размер шрифта в значительной степени игнорируется.Теперь мы можем нарисовать текст динамически масштабируемым шрифтом, в зависимости от высоты изображения:
Раздуть текст, чтобы использовать всю высотуimage
В зависимости от каждого символа у нас теперь может быть промежуток между верхней / нижней стороной изображения и верхней / нижней стороной текста.Способ визуализации или отрисовки глифа сильно зависит от используемого шрифта.Я не являюсь экспертом в области типографики, но AFAIK у каждого шрифта есть свои поля и отступы, а также пользовательские высоты вокруг 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);
Теперь мы можем нарисовать текст, выравнивая его по левой стороне изображения:
Это окончательное изображение теперь имеет шрифт, динамически масштабируемый в зависимости от размераизображение было дополнительно масштабировано и перемещено, чтобы заполнить всю высоту изображения, и было дополнительно перемещено, чтобы не было зазора слева.
Примечание
При рисовании текста DPI из 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
, поскольку данный шрифт может быть недоступен на целевом компьютере.Чтобы получить стабильное автономное решение, разверните шрифт TTF вместе с приложением и установите шрифт вручную с помощью FontCollection
.