CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 12 of 12
  1. #1
    Join Date
    Feb 2001
    Posts
    2,455

    Help with drawing vertical text?

    I need to center text drawn vertically (90 degrees) with a mapping mode of MM_ANISOTROPIC with varying x-y scale factors. The attached project is a sample of my problem. It draws a cross hair in a control and the text should be centered on the cross hair. The text size should also not change regardless of the scaling.

    There is an edit box where you can enter an x and y value for the x and y dimension and pressing OK redraws the screen.

    Please, if someone can look at the attached and give me hints on what I am doing wrong?

    Mike B
    Attached Files Attached Files

  2. #2
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    For those who may like to help but don't want to download the files, in the following code, there is a CSize variable which is a member of the CViewport class, m_szWindow. As you change the m_szWindow.cx / .cy, the text should remain centered and the same text height.

    Currently when you change the values, the text zoom(s) in and out, in other words, gets smaller and larger, and doesn't center correctly.

    Please, any help would be appreciated.

    Code:
    void CViewport::OnPaint()
    {
    	CPaintDC dc(this);
    
    	CRect rc;
    	GetWindowRect(&rc);
    	ScreenToClient(&rc);
    
    	CPen pen(PS_SOLID, 1, RGB(125,125,125));
    	CBrush br(RGB(0,0,0));
    
    	CPen* pOldPen = dc.SelectObject(&pen);
    	CBrush* pOldBrush = dc.SelectObject(&br);
    
    	dc.SetMapMode(MM_ANISOTROPIC);
    	dc.SetWindowExt(m_szWindow.cx, m_szWindow.cy);
    	dc.SetViewportExt(rc.Width(), -rc.Height());
    	dc.SetViewportOrg(rc.left, rc.bottom);
    	// Draw window rect with black background
    	dc.Rectangle(CRect(0,0,m_szWindow.cx,m_szWindow.cy));
    
    	// Draw crosshair where text should be
    	int nX(m_szWindow.cx/2), nY(m_szWindow.cy/2);
    	pen.Detach();
    	pen.CreatePen(PS_SOLID, 1, RGB(255,0,0));
    	dc.SelectObject(&pen);
    
    	dc.MoveTo(0, nY);
    	dc.LineTo(m_szWindow.cx, nY);
    	dc.MoveTo(nX, 0);
    	dc.LineTo(nX, m_szWindow.cy);
    
    	// Create a font, rotate 90 degress
    	LOGFONT lf;
    	ZeroMemory(&lf, sizeof(LOGFONT));
    	lf.lfHeight = 40;
    	lf.lfEscapement = -900;
    	lf.lfOrientation = -900;
    	_tcscpy(lf.lfFaceName, _T("ARIAL"));
    
    	CFont fnt;
    	fnt.CreateFontIndirectW(&lf);
    	CFont* pOldFont = dc.SelectObject(&fnt);
    	
    	// Calculate extents of text
    	CString csTest = _T("Test String");
    	CSize size = dc.GetTextExtent(csTest);
    	
    	dc.SetBkMode(TRANSPARENT);
    	dc.SetTextColor(RGB(0,0,255));
    
    	// Offset center point
    	int nCenterX = nX - (size.cy / 2);
    	int nCenterY = nY - (size.cx / 2);
    
    	dc.TextOutW(nCenterX, nCenterY, csTest);
    
    	dc.SelectObject(pOldPen);
    	dc.SelectObject(pOldBrush);
    	dc.SelectObject(pOldFont);
    }
    Mike B

  3. #3
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    BTW, setting m_szWindow.cx = 1000 and m_szWindow.cy = 500 will center the text. Anything else, it is shifted in one direction or the other.

    Mike B

  4. #4
    Join Date
    Aug 2000
    Location
    New York, NY, USA
    Posts
    5,656

    Re: Help with drawing vertical text?

    Quote Originally Posted by MikeB
    Currently when you change the values, the text zoom(s) in and out, in other words, gets smaller and larger, and doesn't center correctly.
    Not sure about centering, in order to have a constant size in MM_ANISOTROPIC mode, you need something like this:
    Code:
    lf.lfHeight = m_szWindow.cy / 10; // 40;
    i.e. calculate the height based on window extent.
    Vlad - MS MVP [2007 - 2012] - www.FeinSoftware.com
    Convenience and productivity tools for Microsoft Visual Studio:
    FeinWindows - replacement windows manager for Visual Studio, and more...

  5. #5
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    Quote Originally Posted by VladimirF
    Not sure about centering, in order to have a constant size in MM_ANISOTROPIC mode, you need something like this:
    Code:
    lf.lfHeight = m_szWindow.cy / 10; // 40;
    i.e. calculate the height based on window extent.
    Thanks Vladimir. That helps.

    Now I just need to figure out how to center the text. I cannot believe no-one has run into this before.....I have googled every term I can think of and cannot find anything.

    It appears, to me at least, that GetTextExtent(...), DrawText(....DT_CALCRECT), GetTextMetrics(...) all seem to contain a "Stretched" or "Shrunk" version of the text, but the text is drawn correctly. Eg, for the string "1000,2000", the GetTextExtent(...) returns cx = 225 and cy = 214.

    All these functions return measurements based on the string having a 0 escapement/orientation, is that correct?

    Mike B

  6. #6
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    Ok, well, for the centering of the text I am getting close, but not exact. Maybe rounding. I never thought of this for some reason before but I thought the ratio x:y seems to be reflected in how the system calculates the extents of the screen, so I thought I would try the same logic but reverse.

    So I did the following:
    Code:
    // Calculate x and y scale factors
    double x_scale = (double)rcClient.Width() / (double)max(m_szWindow.cx, 1);
    double y_scale = (double)rcClient.Height() / (double)max(m_szWindow.cy, 1);
    
    // get extents of text
    CString csTest = _T("");
    csTest.Format(_T("%d,%d"), m_szWindow.cx, m_szWindow.cy);
    CSize size = dc.GetTextExtent(csTest);
    
    // Offset center point
    int nCenterX = nX - ((size.cy * (1/x_scale) * y_scale)/2);
    int nCenterY = nY - ((size.cx * (1/y_scale) * x_scale)/2);
    
    // Draw text
    dc.TextOutW(nCenterX, nCenterY, csTest);
    Now this seems to be pretty close, but as the scaling ratio is substantially increased / decreased, it seems to be further off center.

    Mike B

  7. #7
    Join Date
    Dec 2005
    Posts
    254

    Re: Help with drawing vertical text?

    All these functions return measurements based on the string having a 0 escapement/orientation, is that correct?
    I don't know if this assumption is correct, but if it isn't, then it could be what's causing the problem. For example, if your string is length 2 inches (for simplicity), and if it is oriented at an angle of 45 degrees, then its horizontal length would only be equal to: cos(PI/4)(2). It's vertical length would be sin(PI/4)(2).

    That is, if GetTextExtent returns the true length of the string, regardless of its angle with respect to the screen. In this case, you would need to take the angle into account in your centering formula. Also, the average height of the characters would need to be included in the calculation when the angle is near 90, since at an angle of 90 degrees (and close to it) the cos is zero or near zero. (In this case, I would just set the width equal to the character height for any angle such that 85 < angle < 95, for example.

    Just an idea.
    Last edited by spiritualfields; April 25th, 2007 at 04:46 PM.

  8. #8
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    Quote Originally Posted by spiritualfields
    I don't know if this assumption is correct, but if it isn't, then it could be what's causing the problem. For example, if your string is length 2 inches (for simplicity), and if it is oriented at an angle of 45 degrees, then its horizontal length would only be equal to: cos(PI/4)(2). It's vertical length would be sin(PI/4)(2).

    That is, if GetTextExtent returns the true length of the string, regardless of its angle with respect to the screen. In this case, you would need to take the angle into account in your centering formula. Also, the average height of the characters would need to be included in the calculation when the angle is near 90, since at an angle of 90 degrees (and close to it) the cos is zero or near zero. (In this case, I would just set the width equal to the character height for any angle such that 85 < angle < 95, for example.

    Just an idea.
    FROM MSDN
    By default, GetTextExtent assumes the text for which it retrieves the dimension is set along a horizontal line (that is, the escapement is 0). If you create a font specifying a non-zero escapement, you must convert the angle of the text explicitly to get the dimensions of the string.
    Does this mean that if you specify an escapement, that GetTextExtent() ignores it and still returns the size based along the horizontal line?

    If this is the case, then my code should work. I have tested it, and although it isn't perfect it is working out to be pretty close.

    Mike B

  9. #9
    Join Date
    Dec 2005
    Posts
    254

    Re: Help with drawing vertical text?

    Your code that does the centering looks okay, and assuming that the crosshairs are centered properly when you zoom in and out, then the entire problem is most likely based around what GetTextExtent is returning. Reading the note associated with it, I would expect that, for vertically aligned texts, to have GetTextExtent return a width equal to the avg height of the characters, and a height that is equal to the true length of the text.

    So it seems that GetTextExtent really returns the width and height of an enclosing rectangle that is always aligned with the x and y axis. What I would do is experiment with this. First, align your test string to the horizontal and get its true length. Also, get its TEXTMETRICS data. Then see what GetTextExtent returns when the test string is vertically aligned. Given the note associated with the function, I would expect a width that is equal to the height of the characters, and a height that is equal to the true length of the test string. If you get a value other than that, then GetTextExtent isn't working exactly as advertised (or as explained in the note). Rotate the test string to various angles (say, 15 degree increments) and observe what GetTextExtent returns. There will most likely be a pattern in the values that is trigonometric in nature. If possible, make the true length of your test string noticeably longer than the avg height of the characters.
    Last edited by spiritualfields; April 26th, 2007 at 11:29 AM.

  10. #10
    Join Date
    Feb 2001
    Posts
    2,455

    Re: Help with drawing vertical text?

    Quote Originally Posted by spiritualfields
    Your code that does the centering looks okay, and assuming that the crosshairs are centered properly when you zoom in and out, then the entire problem is most likely based around what GetTextExtent is returning. Reading the note associated with it, I would expect that, for vertically aligned texts, to have GetTextExtent return a width equal to the avg height of the characters, and a height that is equal to the true length of the text.

    So it seems that GetTextExtent really returns the width and height of an enclosing rectangle that is always aligned with the x and y axis. What I would do is experiment with this. First, align your test string to the horizontal and get its true length. Also, get its TEXTMETRICS data. Then see what GetTextExtent returns when the test string is vertically aligned. Given the note associated with the function, I would expect a width that is equal to the height of the characters, and a height that is equal to the true length of the test string. If you get a value other than that, then GetTextExtent isn't working exactly as advertised (or as explained in the note). Rotate the test string to various angles (say, 15 degree increments) and observe what GetTextExtent returns. There will most likely be a pattern in the values that is trigonometric in nature. If possible, make the true length of your test string noticeably longer than the avg height of the characters.
    Hmmmm, well, here is the code:
    Code:
    	// Create a font, rotate 90 degress
    	LOGFONT lf;
    	ZeroMemory(&lf, sizeof(LOGFONT));
    	lf.lfHeight = m_szWindow.cy / 10;
    	lf.lfEscapement = 0;
    	lf.lfOrientation = 0;
    	_tcscpy(lf.lfFaceName, _T("ARIAL"));
    
    	for(int i = 0; i <= 900; i += 150)
    	{
    		lf.lfEscapement = lf.lfOrientation = -i;
    
    		CFont fnt;
    		fnt.CreateFontIndirectW(&lf);
    		CFont* pOldFont = dc.SelectObject(&fnt);
    
    		// get extents of text
    		CString csTest = _T("Wonder What The Text Size Really Is?");
    		CSize size = dc.GetTextExtent(csTest);
    	
    		dc.SelectObject(pOldFont);
    	}
    And the results
    i == 0...............CSize (cx=785, cy=196)
    i == -150 TO i == -750.........CSize (cx=825, cy=250)
    i == -900..........CSize (cx=828,cy=214)
    Not sure why all the angles from 15 -> 75 degress would result in the same size, but as you can see, cx is always larger then cy.

    The ANISOTROPIC scaling for the above was 1:2 (Window size 1000,2000).

    Mike B

  11. #11
    Join Date
    Dec 2005
    Posts
    254

    Re: Help with drawing vertical text?

    Based on your output, it looks like GetTextExtent is trying (but not succeeding) to always output the true length of the string regardless of the angle. That is actually what I had always thought it did, given the name of the function.

    BUT...why then are the cx and cy values different at different angles??? And they are different in ways that make no sense! Cx is always very large with respect to cy.

    To try to get to the bottom of this, I wrote a little program, using (MM_TEXT mode, which is what I'm familiar with, centered the origin, and wrote some text, varying the angle, and checking on what GetTextExtent returned:

    Code:
    void CMainWindow::OnPaint ()
    {
        CPaintDC dc (this);
        
        CRect rect;
        GetClientRect (&rect);
    
        dc.SetViewportOrg(rect.Width()/2, rect.Height()/2);
        dc.SetBkMode(TRANSPARENT);
        
        CString string = _T("Wonder What The Text Size Really Is?");
    
        for(int i = 0; i < 3600; i += 150)
        {
    
    	CSize size = dc.GetTextExtent(string);
    	TRACE("cx = %d, cy = %d \n", size.cx, size.cy);
    	LOGFONT lf;
    	::ZeroMemory(&lf, sizeof(lf));
    	lf.lfHeight = 160;
    	lf.lfWeight = FW_BOLD;
    	lf.lfEscapement = i;
    	lf.lfOrientation = i;
    	::lstrcpy(lf.lfFaceName, _T("Arial"));
    	CFont font;
    	font.CreatePointFontIndirect(&lf);
    	CFont* pOldFont = dc.SelectObject(&font);
    	dc.TextOut(0,0, string);
    	dc.SelectObject(pOldFont);
    	}
    }
    This is what the TRACE output returned:
    Code:
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16 
    cx = 252, cy = 16
    This is what I would expect...the true length and height of the string regardless of the angle. So, in MM_TEXT mode at least, GetTextExtent is returning the true length of the string. Although I didn't center the text on the screen (the center was the starting place for the text), this little test showed that GetTextExtent does return the true length of the string in MM_TEXT mode. Here is a suggestion. Perhaps your problem has something to do with the mode you're in. Shift to MM_TEXT as a quick check, to see if you are then still having the centering problem.

    EDIT: I think this also means that you WILL have to use some trigonometry in centering the text. But first the problem of why the cx and cy values are different at different angles need to be figured out. Perhaps a problem with the scaling (which I assumed was good as long as the crosshairs were centered on the zooms).
    Last edited by spiritualfields; April 26th, 2007 at 03:00 PM.

  12. #12
    Join Date
    Dec 2005
    Posts
    254

    Re: Help with drawing vertical text?

    Here's another thought. Based on the results that I got from my test of GetTextExtent in MM_TEXT mode, and assuming that in all modes it returns the true length of the string regardless of angle, then the centering solution may not need any trigonometry at all...as long as your text is always vertical. In that case, you would, in your centering calculation, only have to transpose cx and cy. First, however, the problem of why cx and cy are not consistent need to be determined.

    Just throwing out ideas: get the dimensions of a test string in MM_MAP mode...you definitively can rely on that dimension as being true. Now, what scale factors are required in your anistropic mode such that, when they are applied to the anistropic values of cx and cy, always yield the true values of cx and cy (the ones you got in MM_TEXT mode)? Those particular scale factors, where

    cx(MM_ANISTROPIC) * kx = cx(MM_TEXT)
    cy(MM_ANISTROPIC) * ky = cy(MM_TEXT)

    should have the same ratio (kx/ky) in all the zooms (multiplied by a factor equal to the zoom factor). First, though, the MM_ANISTROPIC cx's and cy's need to be made consistent.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured