-3

I'm trying to get the height of a string. I've come across MeasureString but it's inaccurate for what I need it for - too small for what MS Word is showing (okay, this is probably another topic so if you can point me to the right resources...)

So, I found out about TextMetrics. I want to try it but I couldn't understand how to use it. Here is my sample code:

static public double MeasureGroup2()
    {
        TextMetrics txt = new TextMetrics();

        double height;

        height = txt.Height;

        return height;
    }

Where/how can I set the font and font size, and the string for it to return the values I need?

P.S. My end goal is to measure the height of a string the same way MS Word "measures" or renders it. I'm open to other solutions other than these two.

Additional info:

Code for MeasureString:

 static public double MeasureGroup1(string TextFont, int FSize)
    {
        string Str = "Mgkfps";

        Bitmap Bmp = new Bitmap(99,99);
        Graphics DrawGraphics = Graphics.FromImage(Bmp);
        GraphicsUnit Unit = GraphicsUnit.Point;
        DrawGraphics.PageUnit = Unit;
        DrawGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
        StringFormat format = StringFormat.GenericTypographic;

        // Set up string.
        System.Drawing.Font stringFont = new System.Drawing.Font(TextFont, FSize, FontStyle.Regular, Unit);

        // Measure string.
        SizeF stringSize = new SizeF();
        stringSize = DrawGraphics.MeasureString(Str, stringFont, 99, format);

        DrawGraphics.Dispose();
        format.Dispose();
        stringFont.Dispose();


        return stringSize.Height;
        //return TextSize;
    }

Notes: Using Arial 11, and Arial 12, the output of this code needs to be multiplied by 1.0294 to be the same as how MS Word is showing. Physical measurements were made using Word's ruler, Photoshop, and a lot of ratio-and-proportion. (Maybe this is the problem?)

static public double MeasureGroup2(string TextFont, int FSize)
    {
        string Str = "Mgkfps";
        double height;

        Bitmap Bmp = new Bitmap(99, 99);
        Graphics DrawGraphics = Graphics.FromImage(Bmp);

        GraphicsUnit Unit = GraphicsUnit.Point;
        DrawGraphics.PageUnit = Unit;
        DrawGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        StringFormat format = StringFormat.GenericTypographic;

        // Set up string.
        System.Drawing.Font stringFont = new System.Drawing.Font(TextFont, FSize, FontStyle.Regular, Unit);

        Rectangle Rect = new Rectangle(0, 0, 99, 99);

        Size sz = new Size(99,99);
        sz = TextRenderer.MeasureText(DrawGraphics, Str, stringFont, sz ,TextFormatFlags.NoPadding);

        height = sz.Height;

        return height;
    }
Michael
  • 5
  • 6
  • The [TextMetrics Stucture](https://msdn.microsoft.com/en-us/library/system.windows.forms.visualstyles.textmetrics(v=vs.110).aspx) is used to hold the return value of the [VisualStyleRenderer.GetTextMetrics Method](https://msdn.microsoft.com/en-us/library/system.windows.forms.visualstyles.visualstylerenderer.gettextmetrics(v=vs.110).aspx). I don't think that's what you're looking for. – Rufus L Mar 19 '18 at 23:02
  • useful? https://stackoverflow.com/questions/6704923/textrenderer-measuretext-and-graphics-measurestring-mismatch-in-size – pm100 Mar 19 '18 at 23:03
  • "MeasureString is inaccurate" well, no. The way It's used can be inaccurate. However, see [TextRenderer.MeasureText](https://msdn.microsoft.com/en-us/library/system.windows.forms.textrenderer.measuretext(v=vs.110).aspx). Giving suggestions here is difficult. You have not defined a clear context. – Jimi Mar 20 '18 at 01:47
  • @pm100 Yes, thank you. I have read and tested this before but not seen this article. MeasureString gives a closer measurement for my case. – Michael Mar 20 '18 at 15:24
  • @Jimi, "The way It's used can be inaccurate." Interesting. I would like to know more about that. I updated my post to include the code using MeasureString. – Michael Mar 20 '18 at 15:28
  • The first thing to note is that you can't test the size of a string in a defind context using a single letter. Try at least with "MgKpfS". That would be your "Box" (a rectangle) that can contain a real string (upper and lower case). Test this result comparing it to what [`TextRenderer.MeasureText()`](https://msdn.microsoft.com/en-us/library/system.windows.forms.textrenderer.measuretext(v=vs.110).aspx) reports. Also, `Graphics DrawGraphics = Graphics.FromHwnd(IntPtr.Zero);` is inconsistent. If you want to use `Graphics` use the graphics of the device context where that string will be rendered. – Jimi Mar 20 '18 at 16:22
  • The size of the "Box" is defined by a RectangleF => `RectangleF([PointF of Origin], [SizeF of the measure])`. Inches as units is not the best choice. It has to be translated anyway. And you get floating point lost in translation. – Jimi Mar 20 '18 at 16:34
  • Thanks @Jimi, I updated my code to reflect your comments. Did I get the Graphics part right? I also added my MeasureText code. It's returning the same value when I use the TextMetrics code from here: https://www.cyotek.com/blog/retrieving-font-and-text-metrics-using-csharp The problem is that number (18pt for Arial 12) is still far from the 13.8pt I am getting from MS word. Maybe MS Word renders text differently? – Michael Mar 20 '18 at 19:30
  • I forgot to mention, even with the change in code in MeasureString, the output is still the same. – Michael Mar 20 '18 at 19:53
  • No, it's not. I don't even need to test it. Arial 12 in a generic device context will have a Height (Ascending+Descending) of ~13.4. Test it with `SizeF sizeF = [Graphics].MeasureString([Text], [Font], [Bitmap].Size.Width, StringFormat.GenericTypographic);` where [Font] = `Font("Arial", 12, FontStyle.Regular, GraphicsUnit.Point);` and [Graphics] is derived from [Bitmap]. – Jimi Mar 20 '18 at 20:11
  • Note that some more advance programs also count the font bleeding. The part which are allowed to cross/step over the BlackBox. TextRenderer, to work correctly, needs different flags (a lot more) and it returns a measure expressed in Pixels. – Jimi Mar 20 '18 at 20:33
  • Thanks again @Jimi, but that appears to be just the same as my code, right? It gives out the same number. Anyway, I figured out where ~13.8pt came from. I actually should have been using GetLineSpacing. I assumed TextHeight is the same as the line spacing in word, but apparently no. – Michael Mar 20 '18 at 21:47
  • I have no idea what `string TextFont, int FSize` are, so I can't say. The measure for Arial 12 I gave you is correct, Height = `13.40625` (tested, in the meanwhile). If you don't have this value, something is not working as it should. TextRenderer returns `18` Pixels. Which, translated, is corrent too. -- The line spacing in word is only partialy related to the font height. – Jimi Mar 20 '18 at 22:00
  • Yes, the value is correct. But what I actually need is Line Height not Text Height. The 13.8 pt was measured manually, I get 13.4 pt too in my code and the one you gave me. Everything is working perfectly, I was just incorrectly using the MeasureString method to get a value that I thought was something else. – Michael Mar 20 '18 at 22:15

1 Answers1

0

First of all, thank you everyone for helping me out!

When using TextMetrics to get the dimensions of a text, refer to this post here: https://www.cyotek.com/blog/retrieving-font-and-text-metrics-using-csharp

He has a sample project down at the bottom of the page.

As for where I was hoping to use it, which is getting the height of a text in MS Word:

I assumed that the text height and line spacing, when set to single, is the same. This is not the case. There is a small amount of space, the leading, that is not measured by MeasureString. That is why I was hoping to use the GetTextMetrics function since it returns a struct that includes the leading. Anyway, it appears to be used for something else.

Fortunately, and apparently, there is a method native to the .NET framework that returns the value I need which is FontFamily.GetLineSpacing.

LineHeight = stringFont.Size * FF.GetLineSpacing(FontStyle.Regular)/FF.GetEmHeight(FontStyle.Regular);

Reference:

https://answers.microsoft.com/en-us/office/forum/office_2013_release-word/what-reasons-make-different-line-spacing-between/ca1267f9-0cfe-4f26-8048-bbd7a6a46398?auth=1

https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-obtain-font-metrics

Michael
  • 5
  • 6