Мне нужно напечатать числа, в которых некоторые цифры в середине подчеркнуты путем увеличения размера и веса шрифта. В примере ниже подчеркнуто 456
.
Шрифт и два используемых размера настраиваются пользователем.
Текущий код делает это с помощью трех вызовов Graphics.DrawString(...)
.
Проблема, с которой я сталкиваюсь, заключается в том, что с большинством шрифтов я вижу проблемы, связанные с смещением на 1 пиксель (относительно серой линии, 456
находится на лишний пиксель выше, чем остальные цифры):
Я приложил отладочный дамп (формула Боба Пауэлла) для различных шрифтов внизу моего сообщения. Другие методы дали схожие результаты.
Для того чтобы напечатать текст на общей базовой линии, необходимо рассчитать смещение базовой линии для конкретного шрифта
. Я попробовал использовать три метода:
Во-первых, код 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)
Во-вторых, код из: Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?
Наконец, код из поста Боба Пауэлла: http://www.bobpowell.net/formattingtext.htm
Вот мой метод рисования:
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?
Update 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 dpi (и 72 точки на дюйм по определению), 17px, 13px переводятся в 12.75 и 9.75. Вот сравнительный результат:
Обратите внимание, что меньшие шрифты имеют одинаковую высоту на уровне пикселей. Wordpad каким-то образом умудряется сделать это правильно, не округляя размеры шрифтов до удобных значений.