Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Graphics
  4. Measuring of Text with Multiple Fonts in One Single Line

Measuring of Text with Multiple Fonts in One Single Line

Scheduled Pinned Locked Moved Graphics
graphicsc++winformsregex
4 Posts 2 Posters 10 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    paul1167
    wrote on last edited by
    #1

    I am following a request to write C++ code for a label that contains several text elements in one single line, varying by font, size, color.... Ok, that can be done easily in GDI+ by measuring each element's width and then execute a DrawString for each of the text elements starting at its calculated position. So far, I failed miserably. :confused::mad: The horizontal text positions did not appear to be correct. I reverted now to very simple text measuring tests which confused me even more. Test 1: Use of MeasureString With the same font, the width of the string "MM" does not match the double with of the string "M". This cannot be explained with eventual rounding problems. Test2: Use of MeasureCharacterRanges Used the same font as for the first test. The width of "MM" is now exactly double of the width of "M". But: The width of the "M" ist lightyears away from the measurement result in the first test. Here's the code I have used for the two tests:

    void ApplWindow_TextDrawTest(HDC hDC)
    {
    Gdiplus::Graphics *G = new Gdiplus::Graphics(hDC);
    G->SetTextRenderingHint(TextRenderingHint::TextRenderingHintClearTypeGridFit);

    Gdiplus::StringFormat MyFormat;
    MyFormat.SetAlignment(Gdiplus::StringAlignment::StringAlignmentNear);
    MyFormat.SetFormatFlags(Gdiplus::StringFormatFlags::StringFormatFlagsNoWrap);
    
    Gdiplus::Font TextFont(L"Calibri", 36, Gdiplus::FontStyle::FontStyleBold, Gdiplus::Unit::UnitPoint);
    
    const wchar\_t \*Text1M = L"M";
    Gdiplus::PointF TextOrigin1M(0, 0);
    Gdiplus::RectF TextBounds1M;
    
    const wchar\_t \*Text2M = L"MM";
    Gdiplus::PointF TextOrigin2M(0, 50);
    Gdiplus::RectF TextBounds2M;
    
    //--- Test #1:  using MeasureString ----------
    G->MeasureString(Text1M, (INT)wcslen(Text1M), &TextFont, TextOrigin1M, &MyFormat, &TextBounds1M);
    G->MeasureString(Text2M, (INT)wcslen(Text2M), &TextFont, TextOrigin2M, &MyFormat, &TextBounds2M);
    //--- Results:  Text 1 Width=  59.218  ("M")
    //---           Text 2 Width= 102.427  ("MM")
    
    //--- Test #2:  using MeasureCharacterRanges ----------
    Gdiplus::Status RCode;
    Gdiplus::RectF LayoutRect(0, 0, 1000, 100);
    Gdiplus::Region RegionsList\[3\];
    Gdiplus::CharacterRange CRanges\[3\];
    CRanges\[0\].First = 0; CRanges\[0\].Length = 1;
    CRanges\[1\].First = 1; CRanges\[1\].Length = 1;
    CRanges\[2\].First = 0; CRanges\[2\].Length = 2;
    MyFormat.SetMeasurableCharacterRanges(3, CRanges);
    G->MeasureCharacterRanges(Text2M, (INT)wcslen(Text2M), &TextFont, LayoutRect, &MyFormat, 3, RegionsList);
    RCode = RegionsList\[0\].GetBounds(&Text
    
    P 1 Reply Last reply
    0
    • P paul1167

      I am following a request to write C++ code for a label that contains several text elements in one single line, varying by font, size, color.... Ok, that can be done easily in GDI+ by measuring each element's width and then execute a DrawString for each of the text elements starting at its calculated position. So far, I failed miserably. :confused::mad: The horizontal text positions did not appear to be correct. I reverted now to very simple text measuring tests which confused me even more. Test 1: Use of MeasureString With the same font, the width of the string "MM" does not match the double with of the string "M". This cannot be explained with eventual rounding problems. Test2: Use of MeasureCharacterRanges Used the same font as for the first test. The width of "MM" is now exactly double of the width of "M". But: The width of the "M" ist lightyears away from the measurement result in the first test. Here's the code I have used for the two tests:

      void ApplWindow_TextDrawTest(HDC hDC)
      {
      Gdiplus::Graphics *G = new Gdiplus::Graphics(hDC);
      G->SetTextRenderingHint(TextRenderingHint::TextRenderingHintClearTypeGridFit);

      Gdiplus::StringFormat MyFormat;
      MyFormat.SetAlignment(Gdiplus::StringAlignment::StringAlignmentNear);
      MyFormat.SetFormatFlags(Gdiplus::StringFormatFlags::StringFormatFlagsNoWrap);
      
      Gdiplus::Font TextFont(L"Calibri", 36, Gdiplus::FontStyle::FontStyleBold, Gdiplus::Unit::UnitPoint);
      
      const wchar\_t \*Text1M = L"M";
      Gdiplus::PointF TextOrigin1M(0, 0);
      Gdiplus::RectF TextBounds1M;
      
      const wchar\_t \*Text2M = L"MM";
      Gdiplus::PointF TextOrigin2M(0, 50);
      Gdiplus::RectF TextBounds2M;
      
      //--- Test #1:  using MeasureString ----------
      G->MeasureString(Text1M, (INT)wcslen(Text1M), &TextFont, TextOrigin1M, &MyFormat, &TextBounds1M);
      G->MeasureString(Text2M, (INT)wcslen(Text2M), &TextFont, TextOrigin2M, &MyFormat, &TextBounds2M);
      //--- Results:  Text 1 Width=  59.218  ("M")
      //---           Text 2 Width= 102.427  ("MM")
      
      //--- Test #2:  using MeasureCharacterRanges ----------
      Gdiplus::Status RCode;
      Gdiplus::RectF LayoutRect(0, 0, 1000, 100);
      Gdiplus::Region RegionsList\[3\];
      Gdiplus::CharacterRange CRanges\[3\];
      CRanges\[0\].First = 0; CRanges\[0\].Length = 1;
      CRanges\[1\].First = 1; CRanges\[1\].Length = 1;
      CRanges\[2\].First = 0; CRanges\[2\].Length = 2;
      MyFormat.SetMeasurableCharacterRanges(3, CRanges);
      G->MeasureCharacterRanges(Text2M, (INT)wcslen(Text2M), &TextFont, LayoutRect, &MyFormat, 3, RegionsList);
      RCode = RegionsList\[0\].GetBounds(&Text
      
      P Offline
      P Offline
      paul1167
      wrote on last edited by
      #2

      Update I have added one more test which is using standard GDI, and I have changed the GDI+ font to units of pixels. Unfortunately, this third test shows results which do not match the previous results at all. Does anybody have an idea why I get so different results? Since I had not only added a third test but also changed the code of the first tests I am appending the full example here; the results I found have been added as comments:

      void ApplWindow_TextDrawTest(HDC hDC)
      {
      Gdiplus::Graphics *G = new Gdiplus::Graphics(hDC);
      G->SetTextRenderingHint(TextRenderingHint::TextRenderingHintClearTypeGridFit);

      Gdiplus::StringFormat MyFormat;
      MyFormat.SetAlignment(Gdiplus::StringAlignment::StringAlignmentNear);
      MyFormat.SetFormatFlags(Gdiplus::StringFormatFlags::StringFormatFlagsNoWrap);
      
      Gdiplus::Font TextFont(L"Calibri", 36, Gdiplus::FontStyle::FontStyleBold, Gdiplus::Unit::UnitPixel);
      
      const wchar\_t \*Text1M = L"M";
      Gdiplus::PointF TextOrigin1M(0, 0);
      Gdiplus::RectF TextBounds1M;
      
      const wchar\_t \*Text2M = L"MM";
      Gdiplus::PointF TextOrigin2M(0, 50);
      Gdiplus::RectF TextBounds2M;
      
      //--- Test #1:  using MeasureString ----------
      G->MeasureString(Text1M, (INT)wcslen(Text1M), &TextFont, TextOrigin1M, &MyFormat, &TextBounds1M);
      G->MeasureString(Text2M, (INT)wcslen(Text2M), &TextFont, TextOrigin2M, &MyFormat, &TextBounds2M);
      //--- Results:  Text 1 Width= 44.414  ("M")
      //---           Text 2 Width= 76.828  ("MM")
      
      //--- Test #2:  using MeasureCharacterRanges ----------
      Gdiplus::Status RCode;
      Gdiplus::RectF LayoutRect(0, 0, 1000, 100);
      Gdiplus::Region RegionsList\[3\];
      Gdiplus::CharacterRange CRanges\[3\];
      CRanges\[0\].First = 0; CRanges\[0\].Length = 1;
      CRanges\[1\].First = 1; CRanges\[1\].Length = 1;
      CRanges\[2\].First = 0; CRanges\[2\].Length = 2;
      MyFormat.SetMeasurableCharacterRanges(3, CRanges);
      G->MeasureCharacterRanges(Text2M, (INT)wcslen(Text2M), &TextFont, LayoutRect, &MyFormat, 3, RegionsList);
      RCode = RegionsList\[0\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M")
      RCode = RegionsList\[1\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M"; the second char)
      RCode = RegionsList\[2\].GetBounds(&TextBounds2M, G);		// Result: Text 2 Width = 64.000  ("MM")
      
      //--- Test #3:  using the good old GDI ----------
      int MapModeResult = SetMapMode(hDC, MM\_TEXT);			// MM\_TEXT is equivalent to Unit::UnitPixel?
      HFONT TextFont3 = CreateFont(36, 0, 0, 0, FW\_BOLD, false, false, false, ANSI\_CHARSET, OUT\_TT\_ONLY\_PRECIS, C
      
      P P 2 Replies Last reply
      0
      • P paul1167

        Update I have added one more test which is using standard GDI, and I have changed the GDI+ font to units of pixels. Unfortunately, this third test shows results which do not match the previous results at all. Does anybody have an idea why I get so different results? Since I had not only added a third test but also changed the code of the first tests I am appending the full example here; the results I found have been added as comments:

        void ApplWindow_TextDrawTest(HDC hDC)
        {
        Gdiplus::Graphics *G = new Gdiplus::Graphics(hDC);
        G->SetTextRenderingHint(TextRenderingHint::TextRenderingHintClearTypeGridFit);

        Gdiplus::StringFormat MyFormat;
        MyFormat.SetAlignment(Gdiplus::StringAlignment::StringAlignmentNear);
        MyFormat.SetFormatFlags(Gdiplus::StringFormatFlags::StringFormatFlagsNoWrap);
        
        Gdiplus::Font TextFont(L"Calibri", 36, Gdiplus::FontStyle::FontStyleBold, Gdiplus::Unit::UnitPixel);
        
        const wchar\_t \*Text1M = L"M";
        Gdiplus::PointF TextOrigin1M(0, 0);
        Gdiplus::RectF TextBounds1M;
        
        const wchar\_t \*Text2M = L"MM";
        Gdiplus::PointF TextOrigin2M(0, 50);
        Gdiplus::RectF TextBounds2M;
        
        //--- Test #1:  using MeasureString ----------
        G->MeasureString(Text1M, (INT)wcslen(Text1M), &TextFont, TextOrigin1M, &MyFormat, &TextBounds1M);
        G->MeasureString(Text2M, (INT)wcslen(Text2M), &TextFont, TextOrigin2M, &MyFormat, &TextBounds2M);
        //--- Results:  Text 1 Width= 44.414  ("M")
        //---           Text 2 Width= 76.828  ("MM")
        
        //--- Test #2:  using MeasureCharacterRanges ----------
        Gdiplus::Status RCode;
        Gdiplus::RectF LayoutRect(0, 0, 1000, 100);
        Gdiplus::Region RegionsList\[3\];
        Gdiplus::CharacterRange CRanges\[3\];
        CRanges\[0\].First = 0; CRanges\[0\].Length = 1;
        CRanges\[1\].First = 1; CRanges\[1\].Length = 1;
        CRanges\[2\].First = 0; CRanges\[2\].Length = 2;
        MyFormat.SetMeasurableCharacterRanges(3, CRanges);
        G->MeasureCharacterRanges(Text2M, (INT)wcslen(Text2M), &TextFont, LayoutRect, &MyFormat, 3, RegionsList);
        RCode = RegionsList\[0\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M")
        RCode = RegionsList\[1\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M"; the second char)
        RCode = RegionsList\[2\].GetBounds(&TextBounds2M, G);		// Result: Text 2 Width = 64.000  ("MM")
        
        //--- Test #3:  using the good old GDI ----------
        int MapModeResult = SetMapMode(hDC, MM\_TEXT);			// MM\_TEXT is equivalent to Unit::UnitPixel?
        HFONT TextFont3 = CreateFont(36, 0, 0, 0, FW\_BOLD, false, false, false, ANSI\_CHARSET, OUT\_TT\_ONLY\_PRECIS, C
        
        P Offline
        P Offline
        PaulB48
        wrote on last edited by
        #3

        You should try -36 as font size in your test#3, this matches the font size chosen in the other tests.

        Try better

        1 Reply Last reply
        0
        • P paul1167

          Update I have added one more test which is using standard GDI, and I have changed the GDI+ font to units of pixels. Unfortunately, this third test shows results which do not match the previous results at all. Does anybody have an idea why I get so different results? Since I had not only added a third test but also changed the code of the first tests I am appending the full example here; the results I found have been added as comments:

          void ApplWindow_TextDrawTest(HDC hDC)
          {
          Gdiplus::Graphics *G = new Gdiplus::Graphics(hDC);
          G->SetTextRenderingHint(TextRenderingHint::TextRenderingHintClearTypeGridFit);

          Gdiplus::StringFormat MyFormat;
          MyFormat.SetAlignment(Gdiplus::StringAlignment::StringAlignmentNear);
          MyFormat.SetFormatFlags(Gdiplus::StringFormatFlags::StringFormatFlagsNoWrap);
          
          Gdiplus::Font TextFont(L"Calibri", 36, Gdiplus::FontStyle::FontStyleBold, Gdiplus::Unit::UnitPixel);
          
          const wchar\_t \*Text1M = L"M";
          Gdiplus::PointF TextOrigin1M(0, 0);
          Gdiplus::RectF TextBounds1M;
          
          const wchar\_t \*Text2M = L"MM";
          Gdiplus::PointF TextOrigin2M(0, 50);
          Gdiplus::RectF TextBounds2M;
          
          //--- Test #1:  using MeasureString ----------
          G->MeasureString(Text1M, (INT)wcslen(Text1M), &TextFont, TextOrigin1M, &MyFormat, &TextBounds1M);
          G->MeasureString(Text2M, (INT)wcslen(Text2M), &TextFont, TextOrigin2M, &MyFormat, &TextBounds2M);
          //--- Results:  Text 1 Width= 44.414  ("M")
          //---           Text 2 Width= 76.828  ("MM")
          
          //--- Test #2:  using MeasureCharacterRanges ----------
          Gdiplus::Status RCode;
          Gdiplus::RectF LayoutRect(0, 0, 1000, 100);
          Gdiplus::Region RegionsList\[3\];
          Gdiplus::CharacterRange CRanges\[3\];
          CRanges\[0\].First = 0; CRanges\[0\].Length = 1;
          CRanges\[1\].First = 1; CRanges\[1\].Length = 1;
          CRanges\[2\].First = 0; CRanges\[2\].Length = 2;
          MyFormat.SetMeasurableCharacterRanges(3, CRanges);
          G->MeasureCharacterRanges(Text2M, (INT)wcslen(Text2M), &TextFont, LayoutRect, &MyFormat, 3, RegionsList);
          RCode = RegionsList\[0\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M")
          RCode = RegionsList\[1\].GetBounds(&TextBounds1M, G);		// Result: Text 1 Width = 32.000  ("M"; the second char)
          RCode = RegionsList\[2\].GetBounds(&TextBounds2M, G);		// Result: Text 2 Width = 64.000  ("MM")
          
          //--- Test #3:  using the good old GDI ----------
          int MapModeResult = SetMapMode(hDC, MM\_TEXT);			// MM\_TEXT is equivalent to Unit::UnitPixel?
          HFONT TextFont3 = CreateFont(36, 0, 0, 0, FW\_BOLD, false, false, false, ANSI\_CHARSET, OUT\_TT\_ONLY\_PRECIS, C
          
          P Offline
          P Offline
          paul1167
          wrote on last edited by
          #4

          I think I found the answer to my question: GDI and GDI+ do both not resolve by fractions of pixels. That is mainly why my measurement result and the rendered output do not match in all situations. I have now received an advice from a friend to take a closer look into the features of DirectWrite. Indeed, this engine appears to be the solution for my demands.

          1 Reply Last reply
          0
          Reply
          • Reply as topic
          Log in to reply
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes


          • Login

          • Don't have an account? Register

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • World
          • Users
          • Groups