1

I draw a string on a canvas, using GDI+ in C++. Is there any API to get the out layer (width, height ) of a string in a certain font? Thanks very much! Many thanks to Windows programmer's solution. I wrote the following code.

    Bitmap bitmap(1000,1000);
    Graphics graphics(&bitmap);
    RectF rec;
    RectF useless;
    graphics.MeasureString(m_sWords, -1, m_pFont.get(), useless, &rec);
    int WordWidth = rec.Width + 1;
    int WordHeight Height = rec.Height + 1;

Need I use a real graphics to call MeasureString? Is there any way to get wordwidth,wordheight, without create a large Graphics Instance? I found it is resource comsuming.

Pondidum
  • 11,457
  • 8
  • 50
  • 69
user25749
  • 4,825
  • 14
  • 61
  • 83

3 Answers3

2

Graphics::MeasureString computes an approximation.

Windows programmer
  • 7,871
  • 1
  • 22
  • 23
  • There is also [Graphics::MeasureCharacterRanges](https://learn.microsoft.com/en-us/windows/win32/api/gdiplusgraphics/nf-gdiplusgraphics-graphics-measurecharacterranges) – bobobobo Sep 18 '21 at 17:42
1

Unfortunately you do need to use a Graphics object to do this.

The C# code I use (which returns a RectangleF, because I want to know both the width and the height) is as follows:

/// <summary> The text bounding box. </summary>
private static readonly RectangleF __boundingBox = new RectangleF(29, 25, 90, 40);

/// <summary>
///    Gets the width of a given string, in the given font, with the given
///    <see cref="StringFormat"/> options.
/// </summary>
/// <param name="text">The string to measure.</param>
/// <param name="font">The <see cref="Font"/> to use.</param>
/// <param name="fmt">The <see cref="StringFormat"/> to use.</param>
/// <returns> The floating-point width, in pixels. </returns>
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) __boundingBox.Width, (int) __boundingBox.Height)))
   {
      Region[] regions = g.MeasureCharacterRanges(text, font,
         __boundingBox, myFormat);
      return regions[0].GetBounds(g);
   }
}

This will return a RectangleF of the size of the entire text string, word-wrapped as necessary, according to the bounding box specified as __boundingBox. On the plus side, the Graphics object is destroyed as soon as the using statement is complete…

As an aside, GDI+ seems to be pretty unreliable at this; I've found it to be quite buggy (see my question “Graphics.MeasureCharacterRanges giving wrong size calculations in C#.Net?”, for example). If you can use TextRenderer.DrawText from System.Windows.Forms, then do.

Community
  • 1
  • 1
Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
1

To make the picture complete: it can simply be done with a GraphicsPath, which is better since it requires no DeviceContext:

Dim p As New GraphicsPath

Using stringFormat As New StringFormat()
    stringFormat.Trimming = StringTrimming.EllipsisCharacter
    stringFormat.LineAlignment = StringAlignment.Center
    stringFormat.Alignment = StringAlignment.Near

    p.AddString(text, font.FontFamily, font.Style, font.SizeInPoints, Point.Empty, stringFormat)
End Using

Return p.GetBounds.Size

Where text is a given string and font a given font. Returns a SizeF-Structure. I've found the results to be much more precise than Graphics.MeasureString aka GdipMeasureString-API.

McZ
  • 41
  • 2