GDI + рисунок текста с различными размерами на базовой линии имеет проблемы, связанные с 1px
Мне нужно напечатать числа, где некоторые цифры в середине подчеркиваются увеличением размера шрифта и веса. В приведенном ниже примере подчеркивается 456
.
![enter image description here]()
Шрифт и два используемых размера настраиваются пользователем.
Текущий код делает это, используя три вызова Graphics.DrawString(...)
.
Проблема, с которой я сталкиваюсь, заключается в том, что с большинством шрифтов я вижу проблемы с одним пикселем (относительно серой линии, 456
сидит дополнительный пиксель выше других цифр):
![enter image description here]()
Я добавил несколько отладочных дампов (формулы Боба Пауэлла) для разных шрифтов в нижней части моего сообщения. Другие методы дали аналогичные результаты.
Чтобы распечатать текст на общей базовой линии, необходимо вычислить смещение базовой линии для конкретного Font
. Я пробовал использовать три метода:
Во-первых, код MSDN: http://msdn.microsoft.com/en-us/library/xwf9s90b(v=vs.80).aspx
ascent = fontFamily.GetCellAscent(FontStyle.Regular);
ascentPixel = font.Size * ascent / fontFamily.GetEmHeight(FontStyle.Regular)
Во-вторых, код из: Используя GDI +, какой самый простой способ выровнять текст (в нескольких разных шрифтах) по общей базовой линии?
Наконец, код из сообщения Боба Пауэлла: http://www.bobpowell.net/formattingtext.htm
Здесь мой метод draw:
private void DrawOnBaseline(Graphics g, string text, FontWithBaseline fwb, Brush brush, float x, float y) {
g.DrawString(text, fwb.Font, brush, x, y - fwb.Baseline, StringFormat.GenericTypographic);
}
Где FontWithBaseline
просто связывает шрифт со своим соответствующим вычислением базовой линии:
public class FontWithBaseline {
private Font m_font;
private float m_baseline;
public FontWithBaseline(Font font) {
m_font = font;
m_baseline = CalculateBaseline(font);
}
public Font Font { get { return m_font; } }
public float Baseline { get { return m_baseline; } }
private static float CalculateBaseline(Font font) {
... // I've tried the three formulae here.
}
}
Я еще не экспериментировал с Graphics.TestRenderingHint
. Это волшебный соус?
Что мне не хватает? Есть ли альтернативный API, который я могу использовать, когда я вызываю вызов для рисования, снабжает базовую координату Y?
![enter image description here]()
Обновление 1
Я интерполировал свой код с помощью @LarsTech. Он делал одно тонко другое; он добавлял 0.5f
. Однако даже этот вариант не устраняет проблему. Здесь код:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
TryLarsTechnique(e);
}
private void TryLarsTechnique(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
GraphicsContainer c = g.BeginContainer();
g.Clear(Color.White);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
Font small = new Font("Arial", 13, FontStyle.Regular, GraphicsUnit.Pixel);
Font large = new Font("Arial", 17, FontStyle.Bold, GraphicsUnit.Pixel);
int x = 100;
int y = 100;
x += DrawLars(g, "12.3", small, x, y);
x += DrawLars(g, "456", large, x, y);
x += DrawLars(g, "8", small, x, y);
g.EndContainer(c);
}
// returns width of text
private int DrawLars(Graphics g, string text, Font font, int x, int y) {
float offset = font.SizeInPoints /
font.FontFamily.GetEmHeight(font.Style) *
font.FontFamily.GetCellAscent(font.Style);
float pixels = g.DpiY / 72f * offset;
int numTop = y - (int)(pixels + 0.5f);
TextRenderer.DrawText(g, text, font, new Point(x, numTop), Color.Black, Color.Empty, TextFormatFlags.NoPadding);
return TextRenderer.MeasureText(g, text, font, Size.Empty, TextFormatFlags.NoPadding).Width;
}
Мне интересно, является ли определение размера шрифта с помощью GraphicsUnit.Pixel
виновником. Возможно, есть способ найти предпочтительные размеры для любого конкретного шрифта?
Обновление 2
Я попытался указать размеры шрифта в точках вместо пикселей, и это также не полностью решает проблему. Обратите внимание, что использование только целых точечных размеров в моем случае не является вариантом. Чтобы убедиться, что это возможно, я попробовал это на Windows Wordpad. Размеры Использование 96 точек на дюйм (и 72 точки на дюйм по определению), 17px, 13px перевести на 12,75 и 9,75. Здесь результат сравним:
![enter image description here]()
Обратите внимание, как маленькие шрифты имеют одинаковую высоту на уровне пикселей. Поэтому Wordpad каким-то образом справляется с этим, не округляя размеры шрифта до удобных значений.
Ответы
Ответ 1
У вас недостаточно кода для воспроизведения проблемы, так что вот рабочий пример с использованием примера Боба Пауэлла, который вы дали.
Только демонстрационный код:
private void panel1_Paint(object sender, PaintEventArgs e) {
e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
string[] numbers = new string[] { "1", "2", ".", "3", "4", "5", "6", "8" };
int x = 10;
int y = 30;
foreach (string num in numbers) {
Font testFont;
if (num == "4" || num == "5" || num == "6")
testFont = new Font("Arial", 16, FontStyle.Bold);
else
testFont = new Font("Arial", 11, FontStyle.Regular);
float offset = testFont.SizeInPoints /
testFont.FontFamily.GetEmHeight(testFont.Style) *
testFont.FontFamily.GetCellAscent(testFont.Style);
float pixels = e.Graphics.DpiY / 72f * offset;
int numTop = y - (int)(pixels + 0.5f);
TextRenderer.DrawText(e.Graphics, num, testFont, new Point(x, numTop),
Color.Black, Color.Empty, TextFormatFlags.NoPadding);
x += TextRenderer.MeasureText(e.Graphics, num, testFont,
Size.Empty, TextFormatFlags.NoPadding).Width;
}
e.Graphics.DrawLine(Pens.Red, new Point(5, y + 1), new Point(x + 5, y + 1));
}
Это дает:
![enter image description here]()
Ответ 2
Возможно, проблема в том, что вы указываете размер шрифта в pixels
, в то время как ваше смещение рассчитывается в points
и преобразуется обратно в pixels
. Это может привести к разным неточностям.
Попробуйте указать размер шрифта в points
и посмотрите, работает ли он.
Ответ 3
Если для параметра Graphics.TextRenderingHint
установлено использование сетки, фактические показатели масштабированного шрифта TrueType в пикселях определяются GDI + через поиск шрифта таблица VDMX, а не просто масштабированием и округлением его расчетных показателей. Это то, что я понял, перешагнув Graphics.DrawString при разборке.