Solved

CDC::DrawText( ) returns strange heights??

Posted on 1998-08-02
27
1,546 Views
Last Modified: 2013-11-19
I'm trying to use CDC::DrawText( ) to output text, and it all works marvelously, except for one thing, which I can demonstrate with 4 lines of (non- Wizard) code.

Create a new MFC exe-type project, with all the defaults. Use ClassWizard to add the OnPrint() function to the view class.  Use the following code as the implementation of that OnPrint() function:

CRect rcClip(0, 0, pInfo->m_rectDraw.right, 0);
pDC->DrawText("text", 4, &rcClip, DT_LEFT | DT_CALCRECT);
TRACE1(" * Height is changing: %d\n", rcClip.Height());
CView::OnPrint(pDC, pInfo);

Build the program, run it, and go into print preview mode.  Watch the output window in MSVC as you resize the window frame of the test app: the height changes between 80 and 120 or so!  What is going on??  I thought DrawText( )
set rcClip using logical units, so shouldn't the same text have the same height regardless of what the size of the window is?  Notice that m_rectDraw.bottom is always the same regardless of the window size... what am I missing?  TIA.
0
Comment
Question by:laleonard
  • 11
  • 4
  • 4
  • +4
27 Comments
 
LVL 8

Expert Comment

by:trestan
ID: 1320081
I notice you set 0 in your codes: CRect rcClip(0, 0, pInfo->m_rectDraw.right, 0); If so, there should be no output in the print review. I think it should be pInfo->m_rectDraw.bottom. Also you use DT_CALCRECT, when I use it, no output again. From the online help:
DT_CALCRECT   Determines the width and height of the rectangle. If there are multiple lines of text, DrawText will use the width of the rectangle pointed to by lpRect and extend the base of the rectangle to bound the last line of text. If there is only one line of text, DrawText will modify the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text, but does not draw the text.
I suggest you omit this option and try again. I would like to try to give you further suggestions if necessary.
0
 
LVL 8

Expert Comment

by:trestan
ID: 1320082
I do notice the phenomena you mentioned. Again, I think the height will not change if you do not use DT_CALCRECT. The problem should be the matter of true type font. DT_CALCRECT will modify the rect according to the height and width of the formmatted text. Text will use different points to display properly at various sizes. That is why the Height returned form rect changes a little.
0
 
LVL 2

Expert Comment

by:jstolan
ID: 1320083
With all due respect, I don't agree with trestan's answer.  Why should the height be changing?  DrawText is supposed to return the height of the text, regardless of whether the DT_CALCRECT operation is selected.  Laleonard is not trying to output the text, he is simply trying to demonstrate that this value is changing.

I've noticed the same phenomenon with my DrawText operations.  I have a page which I'm printing (it also happens in the OnDraw function).  I set the font as desired.  I then do a DrawText with the DT_CALCRECT options set.  The Height returned by the first DrawText is not the same as subsequent ones.  

I finally put in a workaround.  I do three DrawText operations.  The Height returned by the last one is always correct.  I'm not sure if I really need to do more than two DrawText procedures or not.  It worked after three, so that was good enough.

I'd love to know what's really going on though.
0
 

Author Comment

by:laleonard
ID: 1320084
Thanks for the rsponses...

Trestan is incorrect when he says that the zero argument is wrong.  The DT_CALCRECT flag sets the "bottom" member of the sent CRect; it doesn't matter what is initially in there.  Also, obviously there is no output when using DT_CALCRECT, but that is immaterial to the fact that the height is being calculated incorrectly.  Also, the incorrect behavior I'm seeing is the same for TrueType and non-TrueType fonts.

jstolen's suggestion of calling DrawText multiple times doesn't seem to work for my little test app.  Do you mean I should call DrawText( ) with DT_CALCRECT three times?  (Now that I think about it, that clearly what you meant.)  Can you post a snippet of code that worked for you?  Thanks!

0
 
LVL 8

Expert Comment

by:trestan
ID: 1320085
I mentioned the "0" if you want to display the text. However, you have known it. As of the different height, it is not the problem of the CRect or the Height() function. If the Rect was not modified according to the text, the height will not change.
So the problem is related with the display of the text, maybe either True type of Non-Truetype. If it is the reason, then there will be no solution to it. However, I suggest you that try to use CDC::GetTextExtent to get the dimension of the text. Pls try to see its value will change or not.
0
 

Author Comment

by:laleonard
ID: 1320086
The problem is, I can't use GetTextExtent( ), because my text wraps to several lines, and GetTextExtent( ) assumes the text is written to one line.
0
 
LVL 8

Expert Comment

by:trestan
ID: 1320087
How about use GetTextMetrics function to fill up the TEXTMETRIC structure and use the font height to fit your needs? Also pls have a look at GetTextExtentExPoint function and make some calculation.
Sorry, I have to leave. Some one else may follow up to give you suggestions. c u later.
0
 
LVL 2

Expert Comment

by:jstolan
ID: 1320088
hmmmm

Yes I just call draw text three times with DT_CALCRECT set each time.  One right after another.  I noticed that when I didn't do that the the text was overwriting earlier lines.  Doing that seemed to fix it.  I don't have a clue as to why it would make a difference though.  I don't have the code here, but I'll take another look at it tomorrow.


0
 
LVL 1

Expert Comment

by:slinky
ID: 1320089
There is also a difference being the printer DC and the screen DC. The print preview (although appearing on the screen) will use the printer DC so will give you a different size as it gets its physical values from a different device.

I have had a similar problem before but only while coding in Windows API.
0
 

Author Comment

by:laleonard
ID: 1320090
If that were true, then why are the "bottom" and "right" members of pInfo->m_rectDraw always the same?  They should also be being affected, but they aren't.
0
 
LVL 1

Expert Comment

by:slinky
ID: 1320091
good question...dunno
The problems I encountered with different DCs were only in with font metrics not positioning
when outputting text to a print preview I had to get a Printer DC not a screen DC
0
 
LVL 6

Expert Comment

by:snoegler
ID: 1320092
I have tried the GetTextExtentExPoint() function - and got exactly the same varying values.
I think that's one of the little errors of the Windows OS :)
0
 

Author Comment

by:laleonard
ID: 1320093
Adjusted points to 400
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 8

Expert Comment

by:Answers2000
ID: 1320094
For print & print preview the font scales so alters the size of the text box.

(1) SetMapMode(MM_TEXT)
(2) Create your own font, and select it
(3) Do draw text
(4) Clean up fonts etc.
0
 
LVL 6

Expert Comment

by:snoegler
ID: 1320095
Answer2000, i've tried that already - that does not work.
0
 

Author Comment

by:laleonard
ID: 1320096
Re Answer2000, it's already in MM_TEXT mode.

The original problem can still be demonstrated with 4 lines of non-Wizard code; increasing the points to 450.
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1320097
Well I tried it and you're right.  So I looked up one of my programs which does print and print preview correctly.

I know this doesn't really explain why, but I do this in my OnPrint

void CTComply::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
      //
      // get size of  page
      //
      int nX = pDC->GetDeviceCaps( HORZRES ) - 1 ; // -1 as some print drivers remove a pixel
      int nY = pDC->GetDeviceCaps( VERTRES ) - 1 ;
      int nXDPI = ( 254 * nX ) / ( 10 * pDC->GetDeviceCaps(HORZSIZE) ) ;
      int nYDPI = ( 254 * nY ) / ( 10 * pDC->GetDeviceCaps(VERTSIZE) ) ;
      int nFontLarge = nYDPI / 6 ;
      int nFontHuge = nYDPI / 3 ;
      int nFontFixed = nYDPI / 6 ;

I then go on to create fonts using these sizes, and everything goes on to work fine on NT, 95, 98 on lots of printers, preview, zoomed in, any size, etc.  This includes lots of calculations, the page size being in nX and nY (arbitary units), or in inches (as it would appear on the paper rather than the screen - so you get a true representation) by using the DPI values and dividing into nX, nY.

0
 

Author Comment

by:laleonard
ID: 1320098
Still no change: DrawText reports a different height for each frame window size.  Here's the only non-Wizard function in the test project, with Answers2000's proposed changes; did I understand what you meant correctly?

void CTest9View::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
      _ASSERTE(MM_TEXT == pDC->GetMapMode());

      // Get font height.  (In MM_TEXT, pixels = logical units = device units)
      int nVertDots = pDC->GetDeviceCaps(VERTRES) - 1;
      int nVertDpi = (254 * nVertDots) / (10 * pDC->GetDeviceCaps(VERTSIZE));
      int nFontPixels = nVertDpi / 6;            // One-sixth of an inch high.
      
      // Create my font and select into DC.
      LOGFONT lf;
      ::ZeroMemory(&lf, sizeof(lf));
      lf.lfHeight = nFontPixels;
      lf.lfWeight = FW_NORMAL;
      lstrcpy(lf.lfFaceName, "Arial");

      CFont myfont;
      VERIFY(myfont.CreatePointFontIndirect(&lf, pDC));
      CFont* pFontOld = pDC->SelectObject(&myfont);

      // Draw my text.  Note that as the frame window size changes, the height
      // reported by DT_CALCRECT changes.
      CRect rc(pInfo->m_rectDraw.left, pInfo->m_rectDraw.top,
        pInfo->m_rectDraw.right, pInfo->m_rectDraw.bottom);
      CString s("Some long text ---> This function calls OleInitialize to initialize OLE. It loads the string ach with the initial Hello message, obtained from the string table through the constant IDS_HelloMessage. Then it calls CHello::Create to create a single, global instance of the application object, passing it the initial Hello message and receiving a value for g_phello, a pointer to the instance. If the function is successful, it returns a value of True.");

      pDC->DrawText(s, s.GetLength(), &rc, DT_CALCRECT);
      TRACE2(" * DrawText is changing: %d (%d", rc.Height(), rc.left);
      TRACE3(", %d, %d, %d)\n", rc.top, rc.right, rc.bottom);

      CSize siz(pDC->GetTextExtent(s));
      TRACE2(" * GetTextExtent is not changing: (%d, %d)\n", siz.cx, siz.cy);

      // Call base method.
      CView::OnPrint(pDC, pInfo);
}

0
 

Author Comment

by:laleonard
ID: 1320099
I finally found a pointer to the solution on DejaNews, in a posting by Tom McDonald on 8/6/98 6:47a.m.: "I encountered the same problem with 95. The difference has to do with the way the LVN_GETDISPINFO message is handled in 95 vs. NT. In Windows 95, it correctly calls the callback to get the text and returns the true text length but the returned text is blank (Windows 95 bug!). I changed my code to get text via GetItem if using callbacks."  The final function I cam up with is this:

// CListCtrl::GetItemText() has a bug in Win95 when callback items are
// used.  This works around that bug.
CString MyListCtrl::GetItemTextWorks(int nItem, int nSubItem) const
{
      CString sText;
      char* pszText = NULL;
      int nBufferSize(32);

      LV_ITEM lvi;
      ::ZeroMemory(&lvi, sizeof(lvi));
      lvi.mask = LVIF_TEXT;
      lvi.iItem = nItem;
      lvi.iSubItem = nSubItem;

      do {
            nBufferSize *= 2;
            lvi.cchTextMax = nBufferSize;
            pszText = sText.GetBufferSetLength(nBufferSize);
            lvi.pszText = pszText;
            VERIFY(GetItem(&lvi));
            sText.ReleaseBuffer();
            pszText = NULL;
            _ASSERTE(nBufferSize == lvi.cchTextMax);

            // If the GetDispInfo handler provided its own buffer
            // (and ignored ours), we detect that here.
            if (lvi.pszText != pszText) {
                  sText = lvi.pszText;
                  break;
            }
      }
      // Sometimes, lvi.pszText comes back exactly full, and sometimes
      // it leaves room for a null.  Another CListCrl bug?
      while (nBufferSize - lstrlen(lvi.pszText) < 2);

      return sText;
}


0
 

Author Comment

by:laleonard
ID: 1320100
This is a bogus comment so that the stupid interface will allow me to delete it.  Sigh.
0
 

Author Comment

by:laleonard
ID: 1320101
Aaarrgghh.
0
 
LVL 7

Expert Comment

by:psdavis
ID: 1320102
laleonard,

Since someone answered the question, you now cannot delete it.  I believe it is possible to recover your points by asking someone in Customer Support to delete it.

However... The people involved in the answering of your question tried their best to answer an unanswerable question to the best of their ability.  Just because the question wasn't answered to your satisfaction shouldn't diminish their time and effort.  

How about..., asking to reduce the points via customer support and asking the one or two people who assisted you the most to answer to this or a extra bogus question to receive their just reward. (although absolutely worthless to us).

Sorry about the soapbox...

Phillip
0
 

Author Comment

by:laleonard
ID: 1320103
The someone who answered my question is me.  I found a clue to the answer in Deja News.  Also, I am suprised at your characterization of my question as "unanswerable"; just because you or I don't know the answer, doesn't make it unanswerable.  Finally, are you suggesting that people should be rewarded on the basis of effort, regardless of whether their proposed answers actually worked?  I think not.

0
 
LVL 7

Expert Comment

by:psdavis
ID: 1320104
You're absolutely right and I fully apologize for the comment.  I am glad that it worked for you (I personally tried to fix it for over four hours one night).  I'm was just getting angered by (absolutely not you in no form or fashion) people that are incredibly ungrateful for the work we're trying to do.

The message was initially just an assistance to you on how to delete the message.  I apologize for inflicting my feelings onto you.  I hope this apology is accepted.

Phillip

0
 
LVL 8

Accepted Solution

by:
Answers2000 earned 450 total points
ID: 1320105
I think I have it!

I (finally) remembered having almost the same program years ago under Win3.1

Could it be that the font's don't scale exactly ? (when you create the font use True Type and exact sizing (Proof Quality) options to make it more accurate, but this is not a 100% fix)

What happens is the font just fits (say) into 2 lines of text.  At a bit bigger size, the font doesn't quite fit into 2 lines, so flows into 3 lines, this being because of rounding errors in the font size!

When our app in question was rewritten after a few years, we hated this problem so much we decide to draw the text into a bitmap at the largest possible size and StretchBlt it down to the approriate size.  This is a work round that works.

If you want to see if this is the problem, put a real call into DrawText and see how it's displayed/printed.  I bet you'll find I'm right.

Oh, and BTW there is no 100% solution sticking 100% with DrawText, because of the rounding errors.  The bitmap/stretchblt work round is the only way round this particular problem.


0
 

Author Comment

by:laleonard
ID: 1320106
(The answer I posted on 8/17/98 was incorrect - I had another problem in mind, and posted the "right" solution to the "wrong" problem.  I've been trying since then to navigate this Web site to fix that, to no avail, and had actually asked them to delete this question, as I figured I wasn't going to get answer.)

That seems to be exactly what the problem is: if I actually draw the text, and slowly enlarge the window, you can see it word-break between "a" and "b", then between "b" and "c", then back to between "a" and "b", etc.  This strongly suggests to me that rounding error is involved.

I think I can fix this now that I know what the problem is. Thanks!

0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1320107
I should has said in my answer

Lowest Quality - e.g. Draft acceptable not "(Proof Quality)"

But apart from that it stays unchanged.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now