Graphics.MeasureCharacterRanges gives incorrect size calculations

I am trying to make some text in a specific part of an image in a Web Forms application. The text will be entered by the user, so I want to change the font size to make sure that it is placed in the bounding box.

I have code that does an excellent job of implementing my conceptual concepts, but now I'm trying to do it against assets from the constructor, which are larger, and I get some odd results.

I do the size calculation as follows:

StringFormat fmt = new StringFormat();
fmt.Alignment = StringAlignment.Center;
fmt.LineAlignment = StringAlignment.Near;
fmt.FormatFlags = StringFormatFlags.NoClip;
fmt.Trimming = StringTrimming.None;

int size = __startingSize;
Font font = __fonts.GetFontBySize(size);

while (GetStringBounds(text, font, fmt).IsLargerThan(__textBoundingBox))
{
    context.Trace.Write("MyHandler.ProcessRequest",
        "Decrementing font size to " + size + ", as size is "
        + GetStringBounds(text, font, fmt).Size()
        + " and limit is " + __textBoundingBox.Size());

    size--;

    if (size < __minimumSize)
    {
        break;
    }

    font = __fonts.GetFontBySize(size);
}

context.Trace.Write("MyHandler.ProcessRequest", "Writing " + text + " in "
    + font.FontFamily.Name + " at " + font.SizeInPoints + "pt, size is "
    + GetStringBounds(text, font, fmt).Size()
    + " and limit is " + __textBoundingBox.Size());

Then I use the following line to render text on an image that I take out of the file system:

g.DrawString(text, font, __brush, __textBoundingBox, fmt);

Where:

  • __fonts- it is PrivateFontCollection,
  • PrivateFontCollection.GetFontBySize is an extension method that returns FontFamily
  • RectangleF __textBoundingBox = new RectangleF(150, 110, 212, 64);
  • int __minimumSize = 8;
  • int __startingSize = 48;
  • Brush __brush = Brushes.White;
  • int size starts at 48 and decreases within this cycle
  • Graphics g SmoothingMode.AntiAlias TextRenderingHint.AntiAlias
  • context System.Web.HttpContext ( ProcessRequest IHttpHandler)

:

private static RectangleF GetStringBounds(string text, Font font,
    StringFormat fmt)  
{  
    CharacterRange[] range = { new CharacterRange(0, text.Length) };  
    StringFormat myFormat = fmt.Clone() as StringFormat;  
    myFormat.SetMeasurableCharacterRanges(range);  

    using (Graphics g = Graphics.FromImage(new Bitmap(
       (int) __textBoundingBox.Width - 1,
       (int) __textBoundingBox.Height - 1)))
    {
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

        Region[] regions = g.MeasureCharacterRanges(text, font,
            __textBoundingBox, myFormat);
        return regions[0].GetBounds(g);
    }  
}

public static string Size(this RectangleF rect)
{
    return rect.Width + "×" + rect.Height;
}

public static bool IsLargerThan(this RectangleF a, RectangleF b)
{
    return (a.Width > b.Width) || (a.Height > b.Height);
}

.

-, , , while . , , Graphics.MeasureCharacterRanges , , . ( , Unicode, , , ). - , , Graphics.MeasureCharacterRanges ( )? , post 2499067.

-, Graphics.MeasureCharacterRanges , . RectangleF , , , , . , - , GetBounds , , .

, __minimumSize while, , 24pt , Graphics.MeasureCharacterRanges , , 122px ( 64px ). , , while 18pt, Graphics.MeasureCharacterRanges , .

:

24, 193 × 122, - 212 × 64
23, - 191 × 117, - 212 × 64
22, 200 × 75 212 × 64
21, - 192 × 71, - 212 × 64
20, 198 × 68, - 212 × 64
19, - 185 × 65, - 212 × 64
VENNEGOOR HESSELINK DIN-Black 18pt, 178 × 61 212 × 64

, Graphics.MeasureCharacterRanges ? , , , , 21pt ( , Paint.Net), , , , .

+5
4

. , , , , , . , , . , , MeasureCharacterRanges, , . ( .)

, , , , . , , , , . , ( , ). , , , , , .

, , , , , . , , , , .

Dictionary<Tuple<string, Font, Brush>, Rectangle> cachedTextBounds = new Dictionary<Tuple<string, Font, Brush>, Rectangle>();
/// <summary>
/// Determines bounds of some text by actually drawing the text to a bitmap and
/// reading the bits to see where it ended up.  Bounds assume you draw at 0, 0.  If
/// drawing elsewhere, you can easily offset the resulting rectangle appropriately.
/// </summary>
/// <param name="text">The text to be drawn</param>
/// <param name="font">The font to use when drawing the text</param>
/// <param name="brush">The brush to be used when drawing the text</param>
/// <returns>The bounding rectangle of the rendered text</returns>
private unsafe Rectangle RenderedTextBounds(string text, Font font, Brush brush) {

  // First check memoization
  Tuple<string, Font, Brush> t = new Tuple<string, Font, Brush>(text, font, brush);
  try {
    return cachedTextBounds[t];
  }
  catch(KeyNotFoundException) {
    // not cached
  }

  // Draw the string on a bitmap
  Rectangle bounds = new Rectangle();
  Size approxSize = TextRenderer.MeasureText(text, font);
  using(Bitmap bitmap = new Bitmap((int)(approxSize.Width*1.5), (int)(approxSize.Height*1.5))) {
    using(Graphics g = Graphics.FromImage(bitmap))
      g.DrawString(text, font, brush, 0, 0);
    // Unsafe LockBits code takes a bit over 10% of time compared to safe GetPixel code
    BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    byte* row = (byte*)bd.Scan0;
    // Find left, looking for first bit that has a non-zero alpha channel, so it not clear
    for(int x = 0; x < bitmap.Width; x++)
      for(int y = 0; y < bitmap.Height; y++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.X = x;
          goto foundX;
        }
  foundX:
    // Right
    for(int x = bitmap.Width - 1; x >= 0; x--)
      for(int y = 0; y < bitmap.Height; y++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Width = x - bounds.X + 1;
          goto foundWidth;
        }
  foundWidth:
    // Top
    for(int y = 0; y < bitmap.Height; y++)
      for(int x = 0; x < bitmap.Width; x++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Y = y;
          goto foundY;
        }
  foundY:
    // Bottom
    for(int y = bitmap.Height - 1; y >= 0; y--)
      for(int x = 0; x < bitmap.Width; x++)
        if(((byte*)bd.Scan0)[y*bd.Stride + 4*x + 3] != 0) {
          bounds.Height = y - bounds.Y + 1;
          goto foundHeight;
        }
  foundHeight:
    bitmap.UnlockBits(bd);
  }
  cachedTextBounds[t] = bounds;
  return bounds;
}
+1

?

fmt.FormatFlags = StringFormatFlags.NoClip;

, . , .

, : (

0

MeasureCharacterRanges. Graphics. , layoutRect parametr - , , , .NET.

, layoutRect ( ), "a" - {Width=8.898438, Height=18.10938} 12pt Ms Sans Serif.

, "X" (, 1.2), {Width=9, Height=19}.

, , X.

0

, 4 , , .

MeasureString AND MeasureCharacterRanges .

: , (int width MeasureString Size.Width boundingRect MeasureCharacterRanges) 0.72. , 0,72, REAL

int measureWidth = Convert.ToInt32((float)width/0.72);
SizeF measureSize = gfx.MeasureString(text, font, measureWidth, format);
float actualHeight = measureSize.Height * (float)0.72;

float measureWidth = width/0.72;
Region[] regions = gfx.MeasureCharacterRanges(text, font, new RectangleF(0,0,measureWidth, format);
float actualHeight = 0;
if(regions.Length>0)
{
    actualHeight = regions[0].GetBounds(gfx).Size.Height * (float)0.72;
}

( ) , -, , ( DrawString) (* 72/100), ACTUAL, , MEASURED, , , . , , , . , , "" .

0

All Articles