Link to home
Start Free TrialLog in
Avatar of laleonard
laleonard

asked on

CDC::DrawText( ) returns strange heights??

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.
Avatar of trestan
trestan
Flag of Canada image

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.
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.
Avatar of jstolan
jstolan

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.
Avatar of laleonard

ASKER

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!

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.
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.
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.
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.


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.
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.
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
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 :)
Adjusted points to 400
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.
Answer2000, i've tried that already - that does not work.
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.
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.

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);
}

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;
}


This is a bogus comment so that the stupid interface will allow me to delete it.  Sigh.
Aaarrgghh.
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
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.

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

ASKER CERTIFIED SOLUTION
Avatar of Answers2000
Answers2000

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
(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!

I should has said in my answer

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

But apart from that it stays unchanged.