none
Measuring text

    Question

  • What is the most efficient way to measure a large number of  short strings?  Specifically, I'd like to determine the display height of each string, given uniform formatting (same font, size, weight, etc.) and the maximum width the string may occupy?

    Thanks!
    Friday, April 04, 2008 2:18 AM

Answers

  • I've solved it.  Here's the code:

    public static double MeasureTextWidth(
                GlyphTypeface glyphTypeface,
                double emSize,
                string s)
            {
                double width = 0;

                for (int i = 0; i < s.Length; i++)
                {
                    char ch = sIdea;
                    ushort glyph = glyphTypeface.CharacterToGlyphMap[ch];
                    double advanceWidth = glyphTypeface.AdvanceWidths[glyph];
                    width += advanceWidth;
                }
                return width * emSize;
            }


    Thanks again to everyone, especially Charles for pointing me into the right direction.
    Saturday, April 19, 2008 10:43 AM
  • formattedtext class is what you will most likely need here.

     

    as for balancing text, you could possibly use the hyphenationenabled property on the folwdocument... but this doesnt really balance the text.... you wll most likly have to measure text and balance them using the length got from the formatted text class.

    Sunday, April 06, 2008 5:16 PM
  • I haven't tried this, but if you're concerned about all the memory allocations with FormattedText, you might be able to get fairly decent approximate text widths using this technique:

     

    From the FontFamily object, the property named FamilyTypefaces gives you a collection of FamilyTypeface objects.  Look at the Stretch, Style, and Weight properties to determine exactly which FamilyTypeface object you want.

     

    The FamilyTypeface class also has a property named DeviceFontCharacterMetrics.  This is an object of type CharacterMetricsDictionary, which you can index with 16-bit character codes to get individual CharacterMetrics objects for each character.  I suspect that all data is relative to the em size.  You'll want to look at the MSDN docs page documenting the BlackBoxWidth property of CharacterMetrics. You'll need to combine that property with LeftSideBearing and RightSideBearing to get a full width for characters appearing within a string. For the first character in a string, you can ignore LeftSideBearing, and for the last character, RightSideBearing.

     

    Then, just add up the character widths for each string.  Why do I say that this is "approximate"? Because in some text displays kerning will decrease the total width of the text.

     

    Like I said, I haven't tried this, so I might be missing something, but I think it's worth a try and I'm curious what you find.

     

     

    Sunday, April 06, 2008 10:46 PM

All replies

  • I'd use FormattedText.  You can use the same Typeface object in successive calls to the FormattedText constructor.

     

    Friday, April 04, 2008 11:22 AM
  • Thanks!  I'm doing that right now, but the FormattedText does not allow you to change the Text property, so I have to create a new object for each string, which seems wasteful, especially if I have a few thousand strings. 

    TextFormatter looks like another alternative, but it relies on callbacks and adds a lot of complexity.

    As an aside, is there a way to easily balance a line of text that breaks onto two lines, so that the two lines are of roughly equal length?
    Friday, April 04, 2008 4:49 PM
  • formattedtext class is what you will most likely need here.

     

    as for balancing text, you could possibly use the hyphenationenabled property on the folwdocument... but this doesnt really balance the text.... you wll most likly have to measure text and balance them using the length got from the formatted text class.

    Sunday, April 06, 2008 5:16 PM
  • I haven't tried this, but if you're concerned about all the memory allocations with FormattedText, you might be able to get fairly decent approximate text widths using this technique:

     

    From the FontFamily object, the property named FamilyTypefaces gives you a collection of FamilyTypeface objects.  Look at the Stretch, Style, and Weight properties to determine exactly which FamilyTypeface object you want.

     

    The FamilyTypeface class also has a property named DeviceFontCharacterMetrics.  This is an object of type CharacterMetricsDictionary, which you can index with 16-bit character codes to get individual CharacterMetrics objects for each character.  I suspect that all data is relative to the em size.  You'll want to look at the MSDN docs page documenting the BlackBoxWidth property of CharacterMetrics. You'll need to combine that property with LeftSideBearing and RightSideBearing to get a full width for characters appearing within a string. For the first character in a string, you can ignore LeftSideBearing, and for the last character, RightSideBearing.

     

    Then, just add up the character widths for each string.  Why do I say that this is "approximate"? Because in some text displays kerning will decrease the total width of the text.

     

    Like I said, I haven't tried this, so I might be missing something, but I think it's worth a try and I'm curious what you find.

     

     

    Sunday, April 06, 2008 10:46 PM
  • That's an interesting idea, but I don't want to re-invent the wheel.  What's your take on TextFormatter?  Any experience with it?
    Sunday, April 06, 2008 11:32 PM
  • TextFormatter is mainly used when you want to implement your own text editor or needs finer grained control over the layout when drawing texts, if you just want to measure the text, go with FormattedText as already pointed out.

    Hope this helps
    Thursday, April 10, 2008 9:43 AM
  • Marco,
    Now that I have your workaround for the MinWidth bug, I'll probably go with FormattedText.  I'm looking to implement functionality that tells me:   "If n lines of text are displayed, what is the minimum width to fully display the text (assuming uniform font size)"

    Thanks again!
    Thursday, April 10, 2008 5:42 PM
  • Interesting idea Charles. I'll try that technique, but unfortunately it will not be for several more months. I'll update this post with my findings.
    Friday, April 11, 2008 5:41 PM
  • Charles,
    What do you think of using GlyphRun?  It has two useful methods:  ComputeAlignmentBox and ComputeInkBoundingBox -- I'm researching the difference between the two.
    Thursday, April 17, 2008 4:05 AM
  • I've solved it.  Here's the code:

    public static double MeasureTextWidth(
                GlyphTypeface glyphTypeface,
                double emSize,
                string s)
            {
                double width = 0;

                for (int i = 0; i < s.Length; i++)
                {
                    char ch = sIdea;
                    ushort glyph = glyphTypeface.CharacterToGlyphMap[ch];
                    double advanceWidth = glyphTypeface.AdvanceWidths[glyph];
                    width += advanceWidth;
                }
                return width * emSize;
            }


    Thanks again to everyone, especially Charles for pointing me into the right direction.
    Saturday, April 19, 2008 10:43 AM