2010-09-28 8 views
7

Perché devo per aumentare la larghezza MeasureString() risultato del 21% size.Width = size.Width * 1.21f; di eludere capo automatico in DrawString()?MeasureString e coulisse differenza

Ho bisogno di una soluzione per ottenere il risultato esatto.

Stesso carattere, stesso formato stringa, stesso testo utilizzato in entrambe le funzioni.


Dalla risposta da 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); 

Il mio codice.

+3

Mostra il codice. Stai facendo qualcosa di sbagliato, il moltiplicatore dovrebbe essere 1.0 –

risposta

5

Il metodo MeasureString() ha riscontrato alcuni problemi, in particolare durante il disegno di caratteri non ASCII. Si prega di provare TextRenderer.MeasureText() invece.

+2

Il metodo 'TextRenderer.MeasureText()' è anche inaffidabile ... – ChocapicSz

+1

@ChocapicSz: Per essere onesto, non ho trovato un metodo di misurazione dei limiti di stringa affidabile, indipendentemente dalla programmazione ambiente ... La cosa divertente è, lo stesso testo, lo stesso tipo di carattere ma dimensioni del carattere diverse possono produrre risultati completamente diversi ... Ho confrontato l'inglese con il rapporto di stringa tradotto con risultati divertenti: per Arial 10 il testo tradotto era più ampio, ma per Arial 18 era più stretto del testo inglese ... C'est la vie. –

-1

È probabile che sia necessario aggiungere il seguente i le bandiere StringFormat:

StringFormatFlags.FitBlackBox 
+0

bisogno di misurare esattamente la stringa, non per consentire sporgenza all'esterno area di posizionamento – peenut

0

This articolo su CodeProject dà due modi per ottenere l'esatta dimensione di caratteri che sono resi da coulisse.

+0

no, solo larghezza, non aiuta con altezza. Comunque è un articolo sbagliato, dato che Graphics.MeasureCharacterRanges aggiunge davvero il padding! – peenut

+0

Usare semplicemente l'overload 'Graphics.MeasureString' con un parametro' StringFormat' (cioè passare 'StringFormat.GenericTypographic') era sufficiente per le mie esigenze di sola larghezza, grazie per il collegamento a quell'articolo! – Thracx

3

Graphics.MeasureString, TextRenderer.MeasureText e Graphics.MeasureCharacterRanges restituiscono una dimensione che include pixel vuoti attorno al glifo per accogliere ascender e discender.

In altre parole, restituiscono l'altezza di "a" uguale all'altezza di "d" (ascendente) o "y" (discensore). Se avete bisogno della vera dimensione del glifo, l'unico modo è quello di disegnare la corda e contare i pixel:

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

Suoni inefficienti, ma è vero: non riusciva a farlo funzionare con nessuna delle due funzioni, tutte includevano il padding. Userò anche bitmap. Grazie! – peenut

2

Se aiuta nessuno, ho trasformato risposta da smirkingman a C#, correggere i bug di memoria (usando - Smaltire) e interruzioni di loop esterni (senza TODO). Ho anche utilizzato il ridimensionamento della grafica (e dei caratteri), quindi l'ho aggiunto anch'io (non ha funzionato diversamente). E restituisce RectangleF, perché volevo posizionare il testo con precisione (con Graphics.DrawText).

Il non perfetto, ma abbastanza buono per il mio scopo codice sorgente:

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

Questo è folle ma mi sembra l'unico modo ... – Robinson

+0

Smaltire la bitmap è un buon suggerimento, ma tutti quei GOTO reek 'principianti'. Sarebbe stato meglio semplicemente incollando il codice VB in http://converter.telerik.com/ e aggiungendo un 'utilizzo' per la bitmap. – smirkingman

+0

@smirkingman GOTO reek 'begginer', se sono utilizzati per i salti incondizionati, ma sono ok se vengono utilizzati per interruzioni di loop a più livelli (come per l'istruzione return). Ad esempio, se riscrivo questo codice in Java, non sarebbe la parola chiave "goto" ma "interruzione". Maggiori informazioni su https://en.wikipedia.org/wiki/Goto#Common_usage_patterns_of_Goto – peenut

-1

provare questa soluzione: http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (trovato al https://stackoverflow.com/a/11708952/908936)

codice:

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