2012-02-01 12 views
10

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

enter image description here

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):

enter image description here

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?

enter image description here


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:

enter image description here

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.

Respuesta

5

No ha mostrado suficiente código para reproducir el problema, por lo que aquí hay un ejemplo de trabajo que usa el ejemplo de Bob Powell que dio.

único código Demostración:

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)); 
} 

Esto produce:

enter image description here

+0

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. –

+0

@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

+0

@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

2

Tal vez el problema es que está especificando el tamaño de fuente en pixels mientras que su desplazamiento se calcula en points y convertida de nuevo en pixels. Esto podría introducir todo tipo de imprecisiones.

Intente especificar los tamaños de fuente en points y vea si funciona.

+0

Acepto que un problema de redondeo podría ser la causa subyacente. Pero necesito abordar eso a nivel programático. Ver mi segunda actualización. Windows WordPad se las arregla para hacer que todo esto funcione ... –

+0

¿Seguro que intentó establecer el tamaño de fuente en puntos? ¿Cuáles fueron tus resultados? Lo estoy preguntando porque podría ser una pista sobre lo que está pasando. – Coincoin

+0

Lo intenté usando puntos. Para ciertos tamaños de fuente, funciona. El problema es que no puedo restringir mi solución a esos tamaños de letra. Mi control necesita soportar los tamaños especificados por el usuario. También necesita admitir proporcionalmente reducir los tamaños cuando el número contiene demasiados dígitos.Si hubiera una forma programática de encontrar 'buenos' tamaños, y había muchos de esos tamaños, podría adoptar ese enfoque. –

1

Cuando Graphics.TextRenderingHint está configurado para utilizar la rejilla apropiado, las métricas reales de una fuente TrueType escalado en píxeles son determinados por GDI + través de la búsqueda de VDMX table fuente, en lugar de simplemente el escalamiento y redondeo sus métricas de diseño. Eso es lo que pensé al pasar por Graphics.DrawString en el desmontaje.

Cuestiones relacionadas