Necesito imprimir números donde algunos de los dígitos en el medio se enfatizan aumentando el tamaño y el peso de la fuente. En el ejemplo a continuación, se enfatiza 456
.El texto de dibujo GDI + con diferentes tamaños en una línea de base tiene problemas de 0x1
La fuente y los dos tamaños utilizados son configurables por el usuario.
El código actual hace esto usando tres llamadas a Graphics.DrawString(...)
.
El problema que estoy teniendo es que con la mayoría de tipos de letra, estoy viendo off-by-1pixel problemas (en relación con la línea gris, el 456
está sentado un píxel adicional más alta que los otros dígitos):
He adjuntado un volcado de depuración (de la fórmula de Bob Powell) para varias fuentes en la parte inferior de mi publicación. Las otras técnicas arrojaron resultados similares.
Para imprimir texto en una línea de base común, se necesita calcular el desplazamiento de línea base para un Font
en particular. He intentado usar tres técnicas:
En primer lugar, el código de 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)
En segundo lugar, el código de: Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?
Por último, el código de puesto de Bob Powell: http://www.bobpowell.net/formattingtext.htm
Aquí está mi dibujar método:
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);
}
Dónde FontWithBaseline
simplemente asocia una fuente con su respectiva línea de base de cálculo:
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.
}
}
no he experimentado con Graphics.TestRenderingHint
todavía. ¿Esa es la salsa mágica? ¿Qué me estoy perdiendo? ¿Existe una API alternativa que pueda usar, donde llamo a la llamada para extraer suministros de la coordenada Y de la línea de base?
Actualización 1
que interpola mi código con @LarsTech. Estaba haciendo una sutilmente diferente; él estaba agregando un 0.5f
. Sin embargo, incluso esta variante no soluciona el problema. Aquí está el código:
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;
}
Me pregunto si la especificación de tamaño de la fuente usando GraphicsUnit.Pixel
es el culpable. Tal vez hay una manera de encontrar en los tamaños preferidos para cualquier fuente en particular?
Actualización 2
he tratado de especificar los tamaños de fuente en puntos en lugar de píxeles, y esto no resuelve totalmente el problema, tampoco. Tenga en cuenta que usar solo tamaños de puntos enteros no es una opción en mi caso. Para ver si esto es posible, intenté esto en Windows Wordpad. Tamaños Usando 96 ppp (y 72 puntos por pulgada por definición), 17px, 13px se traducen a 12.75 y 9.75. Aquí está la salida comparación:
Observe cómo las fuentes más pequeñas tienen la misma altura a nivel de píxel. Así que Wordpad de alguna manera se las arregla para hacer esto correcto sin redondear los tamaños de fuente a valores convenientes.
Gracias por esto. He actualizado mi pregunta para agregar mi código e incorporar su enfoque. La adición de '0.5f' era algo nuevo. Por desgracia, todavía veo el problema. Intente usar tamaños de fuente de 17 píxeles + 13 píxeles; Sospecho que verás el problema también. –
@DilumRanatunga El '0.5f' viene directamente del ejemplo de Bob Powell (ver la parte inferior de esa página vinculada). Sí, usar 'GraphicsUnit.Pixel' lo sube un píxel. No estoy seguro de por qué, pero al dejarlo atrás, los volvió a poner en línea nuevamente. Creo que 'GraphicsUnit.Point' es el valor predeterminado, y al usar eso, funcionó también. – LarsTech
@DilumRanatunga Si cambio el código a GraphicsUnit.Pixel y luego uso '12.75f' y' 9.75' como tamaño de fuente, se alinea correctamente. No creo * WorkPad está haciendo ningún redondeo, creo que solo muestra un número entero para el usuario por conveniencia. Podría estar equivocado. El diseñador de Visual Studio muestra '8.25pt' para la fuente predeterminada' Microsoft Sans Serif', pero si hace clic en el cuadro de diálogo para cambiar la fuente, solo verá números enteros en el menú desplegable. No estoy seguro si eso te ayuda o no. – LarsTech