2010-09-28 4 views
7

почему я должен увеличить ширину MeasureString() результата на 21% size.Width = size.Width * 1.21f; , чтобы избежать Перенос слов в DrawString()?MeasureString и DrawString разница

Мне нужно решение, чтобы получить точный результат.

Тот же шрифт, тот же самый строковый формат, тот же текст, используемый в обеих функциях.


От ответа на 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); 

Мой код.

+3

Показать код. Вы делаете что-то не так, множитель должен быть 1.0 –

ответ

5

Метод MeasureString() имел некоторые проблемы, особенно при рисовании символов, отличных от ASCII. Попробуйте вместо этого использовать TextRenderer.MeasureText().

+2

Метод TextRenderer.MeasureText() также ненадежен ... – ChocapicSz

+1

@ChocapicSz: Честно говоря, я не нашел ни одного надежного метода измерения строковых ограничений, независимо от программирования окружающая среда ... Самое забавное: один и тот же текст, тот же шрифт, но другой размер шрифта может дать совершенно разные результаты ... Я когда-то сравнивал английский с переведенным соотношением строк со смешными результатами: для Arial 10 переведенный текст был шире, но для Arial 18 он был уже, чем английский текст ... C'est la vie. –

-1

Вы, вероятно, необходимо добавить следующее в самых StringFormat флагов:

StringFormatFlags.FitBlackBox 
+0

необходимо точно измерить строку, чтобы не допускать вывеску внешней области позиционирования – peenut

0

This статья на CodeProject дает два пути, чтобы получить точный размер символов, как они визуализируются с помощью DrawString.

+0

нет, только ширина, она не помогает с высотой. В любом случае это неправильная статья, так как Graphics.MeasureCharacterRanges действительно добавляет дополнение! – peenut

+0

Просто использование перегрузки 'Graphics.MeasureString' с параметром' StringFormat' (т. Е. Передача 'StringFormat.GenericTypographic') было достаточным для моих простых, только для ширины запросов - спасибо за ссылку на эту статью! – Thracx

3

Graphics.MeasureString, TextRenderer.MeasureText и Graphics.MeasureCharacterRanges все возвращают размер, который включает в себя пустые пиксели вокруг глифа для размещения восходящих и десендеров.

Другими словами, они возвращают высоту «a» так же, как высота «d» (восходящая) или «y» (descender). Если вам нужен истинный размер глифа, единственный способ сделать строку и считать пиксели:

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

Звучит неэффективно, но это правда: он не мог заставить его работать ни с одной из функций, все они включали дополнение. Будет также использовать растровые изображения. Благодаря! – peenut

2

Если это помогает любому, я трансформировал ответ от smirkingman на C#, исправление ошибок памяти (с помощью - Утилизировать) и наружные петли (без TODO). Я также использовал масштабирование графики (и шрифтов), поэтому добавил, что тоже (не работал иначе). И он возвращает RectangleF, потому что я хотел точно позиционировать текст (с помощью Graphics.DrawText).

не идеальный, но достаточно хорошо для моей цели исходный код:

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

Это безумие, но мне кажется, что это единственный способ ... – Robinson

+0

Устранение растрового изображения - хорошее предложение, но все эти GOTOs reek 'beginner'. Вам было бы лучше просто вставить код VB в http://converter.telerik.com/ и добавить «использование» для растрового изображения. – smirkingman

+0

@smirkingman GOTOs reek 'begginer', если они используются для безусловных переходов, но они в порядке, если они используются для многоуровневых разрывов цикла (то же, что и оператор возврата). Например, если я переписал этот код на Java, это не было бы «goto», но «break» ключевое слово. Читайте больше на https://en.wikipedia.org/wiki/Goto#Common_usage_patterns_of_Goto – peenut

-1

попробовать это решение: http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitation (нашел его на https://stackoverflow.com/a/11708952/908936)

код:

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