#.net #fonts #drawing #system.drawing
#.net #шрифты #рисование #system.drawing
Вопрос:
Используя System.Drawing.Font
, есть ли способ изменить интервал между буквами, точно так же, как вы можете изменить размер шрифта?
Я хочу увеличить интервал между буквами, чтобы соответствовать определенной ширине.
Если это невозможно, есть ли альтернативы для получения желаемого эффекта? Например, есть ли простой способ иметь несколько графиков, которые будут соответствовать моей конкретной ширине?
Комментарии:
1. Где вы используете этот шрифт? Что использует это?
2. Он используется в изображении GDI с
Graphics.DrawString()
3. Нет встроенного механизма
System.Drawing
для рисования текста с отрегулированным интервалом между буквами. Для этого вам придется создать свой собственный код. Начните с просмотраMeasureCharacterRanges
.
Ответ №1:
Я не думаю, что DrawString позволит вам указывать что-либо столь же подробное, как интервал между символами, но я бы создал вспомогательную функцию, которая вычисляет интервал на основе желаемой ширины, а затем рисует каждый символ по размеру.
Попробуйте это и посмотрите, как у вас получится…
public void DrawSpacedText(Graphics g, Font font, Brush brush, PointF point, string text, int desiredWidth)
{
//Calculate spacing
float widthNeeded = 0;
foreach (char c in text)
{
widthNeeded = g.MeasureString(c.ToString(), font).Width;
}
float spacing = (desiredWidth - widthNeeded) / (text.Length - 1);
//draw text
float indent = 0;
foreach (char c in text)
{
g.DrawString(c.ToString(), font, brush, new PointF(point.X indent, point.Y));
indent = g.MeasureString(c.ToString(), font).Width spacing;
}
}
Вероятно, вы могли бы оптимизировать это, чтобы сделать только один вызов MeasureString для каждого символа. Или даже использовать MeasureCharacterRanges для получения массива (который, я считаю, в любом случае более точен)
Редактировать: вот пример использования MeasureCharacterRanges вместо этого…
public void DrawSpacedText(Graphics g, Font font, Brush brush, PointF point, string text, int desiredWidth)
{
//Calculate spacing
float widthNeeded = 0;
Region[] regions = g.MeasureCharacterRanges(text, font, new RectangleF(point, new SizeF(desiredWidth, font.Height 10)), StringFormat.GenericDefault);
float[] widths = new float[regions.Length];
for(int i = 0; i < widths.Length; i )
{
widths[i] = regions[i].GetBounds(g).Width;
widthNeeded = widths[i];
}
float spacing = (desiredWidth - widthNeeded) / (text.Length - 1);
//draw text
float indent = 0;
int index = 0;
foreach (char c in text)
{
g.DrawString(c.ToString(), font, brush, new PointF(point.X indent, point.Y));
indent = widths[index] spacing;
index ;
}
}
Комментарии:
1. 1 Это хороший способ сделать это. Однако наличие постоянного интервала приводит к кернингу текста , чего трудно добиться должным образом. Другими словами, результаты не будут такими визуально приятными , как при правильном кернинге, цитируя Википедию.
2. Это несколько наивный подход, предполагающий, что один символ === глиф. Нет биди, нет лигатур и так далее. Просто нижняя половина таблицы ASCII.
3. @olegz Если единственная цель — измерить строку, а не рисовать ее, будет ли этого достаточно?
4. @pelican_george, ответ «да», если строка не содержит символов CJK, диакритических знаков или вы не хотите, чтобы код был дружественным к i18n
5. Как бы вы подошли к этому, чтобы он был дружественным к i18n?
Ответ №2:
Этот код поддерживает новую строку в тексте и возвращает объект изображения.
public static Image ConvertTextToImage(String text, Font font, Color textColor, int spacing = 0)
{
var textParts = SplitOnBreakLines(text);
// dummy image, just create drawing
var img = new Bitmap(1, 1);
var drawing = Graphics.FromImage(img);
// calculate width, height
var width = 0.0F;
foreach (char c in text)
{
width = drawing.MeasureString(c.ToString(), font).Width;
}
width = (width spacing * (ClearBreakLines(text).Length - 1)) / textParts.Length;
if (width <= 0)
width = 1;
var height = font.Height * textParts.Length;
// clear and create new objects
img.Dispose();
drawing.Dispose();
img = new Bitmap((int)width, (int)height);
drawing = Graphics.FromImage(img);
//Adjust for high quality
drawing.CompositingQuality = CompositingQuality.HighQuality;
drawing.InterpolationMode = InterpolationMode.HighQualityBilinear;
drawing.PixelOffsetMode = PixelOffsetMode.HighQuality;
drawing.SmoothingMode = SmoothingMode.HighQuality;
drawing.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
//paint the background
drawing.Clear(Color.Transparent);
//create a brush for the text
var textBrush = new SolidBrush(textColor);
//write text
var indent = 0.0F;
var point = new Point();
foreach (var textPart in textParts)
{
foreach (char c in textPart)
{
drawing.DrawString(c.ToString(), font, textBrush, new PointF(point.X indent, point.Y));
indent = drawing.MeasureString(c.ToString(), font).Width spacing;
}
indent = 0.0F;
point.Y = font.Height;
}
drawing.Save();
textBrush.Dispose();
drawing.Dispose();
return img;
}
public static string ClearBreakLines(string s)
{
return Regex.Replace(s, @"rn?|n", string.Empty);
}
public static string[] SplitOnBreakLines(string s)
{
return Regex.Split(s, @"rn?|n");
}