2010-09-28 10 views
7

Dlaczego muszę zwiększyć szerokość MeasureString() wynik o 21% size.Width = size.Width * 1.21f; uniknąć Słowo owijane w DrawString()?MeasureString i sznurkiem różnica

Potrzebuję rozwiązania, aby uzyskać dokładny wynik.

Ta sama czcionka, ten sam format, ten sam tekst używany w obu funkcjach.


Od odpowiedzi przez 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); 

mojego kodu.

+3

Pokaż kod. Robisz coś złego, mnożnik powinien być 1.0 –

Odpowiedz

5

Metoda MeasureString() miała pewne problemy, szczególnie podczas rysowania znaków spoza ASCII. Zamiast tego spróbuj użyć TextRenderer.MeasureText().

+2

Metoda 'TextRenderer.MeasureText()' jest także niewiarygodna ... – ChocapicSz

+1

@CococapicSz: Szczerze mówiąc, nie znalazłem pojedynczej niezawodnej metody pomiaru ograniczeń łańcucha znaków, niezależnie od programowania Środowisko ... Zabawne jest to, że ten sam tekst, ta sama czcionka, ale inny rozmiar czcionki może dać zupełnie inne wyniki ... Raz porównałem przekładnię z angielskiego na przetłumaczony stosunek z zabawnymi wynikami: dla Arial 10 przetłumaczony tekst był szerszy, ale dla Arial 18 był węższy niż tekst w języku angielskim ... C'est la vie. –

-1

Prawdopodobnie trzeba dodać następujące The StringFormat flagi:

StringFormatFlags.FitBlackBox 
+0

należy dokładnie zmierzyć sznur, aby nie dopuścić do zwisu poza obszarem pozycjonowania – peenut

0

This artykuł na codeproject daje dwa sposoby, aby uzyskać dokładny rozmiar znaków jak są renderowane przez sznurkiem.

+0

nie, tylko szerokość, nie pomaga w wysokości. W każdym razie jest to niewłaściwy artykuł, ponieważ Graphics.MeasureCharacterRanges naprawdę dodaje dopełnienie! – peenut

+0

Po prostu użycie przeciążenia 'Graphics.MeasureString' z parametrem' StringFormat' (tj. Przekazanie 'StringFormat.GenericTypographic') było wystarczające dla moich prostych potrzeb związanych tylko z szerokością - dzięki za linkowanie do tego artykułu! – Thracx

3

Graphics.MeasureString, TextRenderer.MeasureText i Graphics.MeasureCharacterRanges wszystkie zwracają rozmiar, który zawiera puste piksele wokół glifu w celu dostosowania wznoszących się i malejących.

Innymi słowy, zwracają wysokość "a" jako taką samą wysokość jak "d" (wznoszący) lub "y" (podrzędny). Jeśli potrzebujesz prawdziwego rozmiaru glifów, jedynym sposobem jest zwrócenie ciąg i policzyć piksele:

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

Brzmi nieefektywnie, ale to prawda: nie udało się uzyskać pracy bez żadnej z funkcji, wszystkie zawierały dopełnienie. Używa również bitmap. Dzięki! – peenut

2

Jeśli to pomoże nikogo, przerabiałem odpowiedź od smirkingman do C#, poprawianie błędów pamięci (używając - Usuwać) i przerwy w pętli zewnętrznej (bez TODO). Użyłem także skalowania grafiki (i czcionek), więc dodałem, że też (nie działa inaczej). I zwraca RectangleF, ponieważ chciałem precyzyjnie ustawić tekst (z Graphics.DrawText).

nie idealny, ale wystarczająco dobre dla mojego celu kodu źródłowego:

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

To szaleństwo, ale wydaje mi się, że to jedyny sposób ... – Robinson

+0

Utylizacja bitmapy to dobra sugestia, ale wszystkie te GOTO odory "początkujące". Lepiej byłoby po prostu wkleić kod VB na http://converter.telerik.com/ i dodać "użycie" dla bitmapy. – smirkingman

+0

@smirkingman GOTOs reek 'begginer', jeśli są używane do bezwarunkowych skoków, ale są one ok, jeśli są używane do łamania pętli wielopoziomowych (tak samo jak instrukcja return). Na przykład, jeśli przepisałbym ten kod na język Java, nie byłby to "goto", ale "break". Przeczytaj więcej na https://en.wikipedia.org/wiki/Goto#Common_usage_patterns_of_Goto – peenut

-1

spróbować tego rozwiązania: http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (znaleziono go w https://stackoverflow.com/a/11708952/908936)

Kod:

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