2010-09-28 10 views
7

¿Por qué tengo que aumentar el ancho de MeasureString() resultado en un 21% size.Width = size.Width * 1.21f; para evadir Ajuste de línea en DrawString()?MeasureString y cordón diferencia

Necesito una solución para obtener el resultado exacto.

mismo tipo de letra, el mismo StringFormat, el mismo texto que se utiliza en ambas funciones.


De respuesta por OP:

SizeF size = graphics.MeasureString(element.Currency, Currencyfont, new PointF(0, 0), strFormatLeft); 
    size.Width = size.Width * 1.21f; 
    int freespace = rect.Width - (int)size.Width; 
    if (freespace < ImageSize) { if (freespace > 0) ImageSize = freespace; else ImageSize = 0; } 
    int FlagY = y + (CurrencySize - ImageSize)/2; 
    int FlagX = (freespace - ImageSize)/2; 
    graphics.DrawImage(GetResourseImage(@"Flags." + element.Flag.ToUpper() + ".png"), 
     new Rectangle(FlagX, FlagY, ImageSize, ImageSize)); 
    graphics.DrawString(element.Currency, Currencyfont, Brushes.Black, 
     new Rectangle(FlagX + ImageSize, rect.Y, (int)(size.Width), CurrencySize), strFormatLeft); 

Mi código.

método
+3

Muestra el código. Que está haciendo algo mal, el multiplicador debe ser de 1,0 –

Respuesta

5

MeasureString() tenía algunos problemas, sobre todo en la elaboración de caracteres no ASCII. Por favor, prueba TextRenderer.MeasureText() en su lugar.

+2

El 'TextRenderer.MeasureText()' método también es poco fiable ... – ChocapicSz

+1

@ChocapicSz: Para ser honesto, no he encontrado un solo límites cadena fiable método de medición, independientemente de la programación entorno ... Lo curioso es que el mismo texto, la misma fuente, pero el tamaño de letra diferente puede arrojar resultados completamente diferentes ... Una vez comparé el índice de cadenas de inglés con la traducción con resultados divertidos: para Arial 10, el texto traducido era más ancho, pero para Arial 18 era más estrecho que el texto en inglés ... C'est la vie. –

-1

Lo más probable es necesario agregar la siguiente los StringFormat las banderas:

StringFormatFlags.FitBlackBox 
+0

necesita medir la cuerda exactamente, no permitir el saliente fuera del área de posicionamiento – peenut

0

This artículo sobre CodeProject da dos maneras de obtener el tamaño exacto de los personajes a medida que son prestados por cordón.

+0

no, solo el ancho, no ayuda con la altura. De todos modos, es un artículo equivocado, ya que Graphics.MeasureCharacterRanges realmente agrega relleno. – peenut

+0

Simplemente el uso de la sobrecarga 'Graphics.MeasureString' con un parámetro' StringFormat' (es decir, pasar 'StringFormat.GenericTypographic') fue suficiente para mis necesidades simples, solo de ancho. ¡Gracias por vincular ese artículo! – Thracx

3

Graphics.MeasureString, TextRenderer.MeasureText y Graphics.MeasureCharacterRanges todos devuelven un tamaño que incluye píxeles en blanco alrededor del glifo para acomodar ascendentes y descendentes.

En otras palabras, vuelven la altura de "a" como la misma que la altura de "d" (ascendente) o "y" (descensor). Si necesita el verdadero tamaño del glifo, la única manera es dibujar la cadena y contar los píxeles:

Public Shared Function MeasureStringSize(ByVal graphics As Graphics, ByVal text As String, ByVal font As Font) As SizeF 

    ' Get initial estimate with MeasureText 
    Dim flags As TextFormatFlags = TextFormatFlags.Left + TextFormatFlags.NoClipping 
    Dim proposedSize As Size = New Size(Integer.MaxValue, Integer.MaxValue) 
    Dim size As Size = TextRenderer.MeasureText(graphics, text, font, proposedSize, flags) 

    ' Create a bitmap 
    Dim image As New Bitmap(size.Width, size.Height) 
    image.SetResolution(graphics.DpiX, graphics.DpiY) 

    Dim strFormat As New StringFormat 
    strFormat.Alignment = StringAlignment.Near 
    strFormat.LineAlignment = StringAlignment.Near 

    ' Draw the actual text 
    Dim g As Graphics = graphics.FromImage(image) 
    g.SmoothingMode = SmoothingMode.HighQuality 
    g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit 
    g.Clear(Color.White) 
    g.DrawString(text, font, Brushes.Black, New PointF(0, 0), strFormat) 

    ' Find the true boundaries of the glyph 
    Dim xs As Integer = 0 
    Dim xf As Integer = size.Width - 1 
    Dim ys As Integer = 0 
    Dim yf As Integer = size.Height - 1 

    ' Find left margin 
    Do While xs < xf 
     For y As Integer = ys To yf 
      If image.GetPixel(xs, y).ToArgb <> Color.White.ToArgb Then 
       Exit Do 
      End If 
     Next 
     xs += 1 
    Loop 
    ' Find right margin 
    Do While xf > xs 
     For y As Integer = ys To yf 
      If image.GetPixel(xf, y).ToArgb <> Color.White.ToArgb Then 
       Exit Do 
      End If 
     Next 
     xf -= 1 
    Loop 
    ' Find top margin 
    Do While ys < yf 
     For x As Integer = xs To xf 
      If image.GetPixel(x, ys).ToArgb <> Color.White.ToArgb Then 
       Exit Do 
      End If 
     Next 
     ys += 1 
    Loop 
    ' Find bottom margin 
    Do While yf > ys 
     For x As Integer = xs To xf 
      If image.GetPixel(x, yf).ToArgb <> Color.White.ToArgb Then 
       Exit Do 
      End If 
     Next 
     yf -= 1 
    Loop 

    Return New SizeF(xf - xs + 1, yf - ys + 1) 

End Function 
+0

Suena ineficiente, pero es cierto: no se pudo hacer funcionar sin ninguna de las funciones, todas incluyeron relleno. Usará bitmaps, también. ¡Gracias! – peenut

2

Si se ayuda a nadie, he transformado respuesta de smirkingman a C#, corrigiendo errores de memoria (utilizando - Desechar) y roturas de lazo externo (sin TODO). También utilicé el escalado en gráficos (y fuentes), así que lo agregué también (no funcionó de otra manera). Y devuelve RectangleF, porque quería posicionar el texto con precisión (con Graphics.DrawText).

El no-perfecto, pero lo suficientemente bueno para mi propósito código fuente:

static class StringMeasurer 
{ 
    private static SizeF GetScaleTransform(Matrix m) 
    { 
     /* 
     3x3 matrix, affine transformation (skew - used by rotation) 
     [ X scale,  Y skew,  0 ] 
     [ X skew,  Y scale,  0 ] 
     [ X translate, Y translate, 1 ] 

     indices (0, ...): X scale, Y skew, Y skew, X scale, X translate, Y translate 
     */ 
     return new SizeF(m.Elements[0], m.Elements[3]); 
    } 

    public static RectangleF MeasureString(Graphics graphics, Font f, string s) 
    { 
     //copy only scale, not rotate or transform 
     var scale = GetScaleTransform(graphics.Transform); 

     // Get initial estimate with MeasureText 
     //TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.NoClipping; 
     //Size proposedSize = new Size(int.MaxValue, int.MaxValue); 
     //Size size = TextRenderer.MeasureText(graphics, s, f, proposedSize, flags); 
     SizeF sizef = graphics.MeasureString(s, f); 
     sizef.Width *= scale.Width; 
     sizef.Height *= scale.Height; 
     Size size = sizef.ToSize(); 

     int xLeft = 0; 
     int xRight = size.Width - 1; 
     int yTop = 0; 
     int yBottom = size.Height - 1; 

     // Create a bitmap 
     using (Bitmap image = new Bitmap(size.Width, size.Height)) 
     { 
      image.SetResolution(graphics.DpiX, graphics.DpiY); 

      StringFormat strFormat = new StringFormat(); 
      strFormat.Alignment = StringAlignment.Near; 
      strFormat.LineAlignment = StringAlignment.Near; 

      // Draw the actual text 
      using (Graphics g = Graphics.FromImage(image)) 
      { 
       g.SmoothingMode = graphics.SmoothingMode; 
       g.TextRenderingHint = graphics.TextRenderingHint; 
       g.Clear(Color.White); 
       g.ScaleTransform(scale.Width, scale.Height); 
       g.DrawString(s, f, Brushes.Black, new PointF(0, 0), strFormat); 
      } 
      // Find the true boundaries of the glyph 

      // Find left margin 
      for (; xLeft < xRight; xLeft++) 
       for (int y = yTop; y <= yBottom; y++) 
        if (image.GetPixel(xLeft, y).ToArgb() != Color.White.ToArgb()) 
         goto OUTER_BREAK_LEFT; 
     OUTER_BREAK_LEFT: ; 

      // Find right margin 
      for (; xRight > xLeft; xRight--) 
       for (int y = yTop; y <= yBottom; y++) 
        if (image.GetPixel(xRight, y).ToArgb() != Color.White.ToArgb()) 
         goto OUTER_BREAK_RIGHT; 
     OUTER_BREAK_RIGHT: ; 

      // Find top margin 
      for (; yTop < yBottom; yTop++) 
       for (int x = xLeft; x <= xRight; x++) 
        if (image.GetPixel(x, yTop).ToArgb() != Color.White.ToArgb()) 
         goto OUTER_BREAK_TOP; 
     OUTER_BREAK_TOP: ; 

      // Find bottom margin 
      for (; yBottom > yTop; yBottom--) 
       for (int x = xLeft; x <= xRight; x++) 
        if (image.GetPixel(x, yBottom).ToArgb() != Color.White.ToArgb()) 
         goto OUTER_BREAK_BOTTOM; 
     OUTER_BREAK_BOTTOM: ; 
     } 

     var pt = new PointF(xLeft, yTop); 
     var sz = new SizeF(xRight - xLeft + 1, yBottom - yTop + 1); 
     return new RectangleF(pt.X/scale.Width, pt.Y/scale.Height, 
      sz.Width/scale.Width, sz.Height/scale.Height); 
    } 
} 
+1

Esto es una locura, pero me parece que es la única forma ... – Robinson

+0

Eliminar el mapa de bits es una buena sugerencia, pero todos esos GOTO huelen a "principiante". Habría sido mejor simplemente pegando el código VB en http://converter.telerik.com/ y agregando un 'uso' para el mapa de bits. – smirkingman

+0

@smirkingman GOTOs reek 'begginer', si se usan para saltos incondicionales, pero están bien si se usan para saltos de bucle de niveles múltiples (igual que la declaración de devolución). Por ejemplo, si reescribo este código en Java, no sería "goto" sino palabra clave "break". Lea más en https://en.wikipedia.org/wiki/Goto#Common_usage_patterns_of_Goto – peenut

-1

Pruebe esta solución: http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (encontramos en https://stackoverflow.com/a/11708952/908936)

código:

static public int MeasureDisplayStringWidth(Graphics graphics, string text, Font font) 
{ 
    System.Drawing.StringFormat format = new System.Drawing.StringFormat(); 
    System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, 1000, 1000); 
    var ranges = new System.Drawing.CharacterRange(0, text.Length); 
    System.Drawing.Region[] regions = new System.Drawing.Region[1]; 

    format.SetMeasurableCharacterRanges (new[] {ranges}); 

    regions = graphics.MeasureCharacterRanges (text, font, rect, format); 
    rect = regions[0].GetBounds (graphics); 

    return (int)(rect.Right + 1.0f); 
}