Click to See Complete Forum and Search --> : Tabs and text justification.


Andrea_Rossini
May 6th, 2004, 04:33 AM
Hello!
Actually I wrote my own function to justify a string inside a rectangular block using GetTextExtentPoint32, SetTextJustification and TextOut APIs.

Although it works fine with many strings, the matter is I don't know how to manage tabbed lines of text.

I also included an MsWord example to show you what want to create. The red lines are to show you where I placed the tabs.

Andrea_Rossini
May 6th, 2004, 06:42 AM
I'm getting nearer to the result just using the TabbedTextOut function. Here comes a little question:

I need to calculate the width of a tab.

On MSDN I read:
(from the TabbedTextOut Remarks)

"If the nTabPositions parameter is zero and the lpnTabStopPositions parameter is NULL, tabs are expanded to eight times the average character width."

so I suppose to use this code to compute the width of a tab:


TEXTMETRIC TextMetrics;
GetTextMetrics(hDC, &TextMetrics);
width = TextMetrics.tmAveCharWidth * 8;


but it doesn't work.

ie for an Arial Bold 12 my function returns 64, but the TabbedTextOut uses an 80 pixels width to draw tabs.

Andrea_Rossini
May 6th, 2004, 09:57 AM
Yuk! I solved that problem too!
Just call GetTabbedTextExtent instead of adding the tab size to the GetTextExtentPoint32.

When I complete everything I'll send the piece of code.

Andrea_Rossini
May 6th, 2004, 10:18 AM
Done! here's what I wasted my day for:


int DrawJustifiedText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, int Align)
{
int x, y;
int w, h;
int i, j;
SIZE Size;
char *cLineStart, *cStart, *cEnd;
TEXTMETRIC TextMetrics;

GetTextMetrics(hDC, &TextMetrics);
h = TextMetrics.tmHeight;

y = lpRect->top;
cLineStart = cStart = (char*)lpString;
j = -1;

switch(Align)
{
case -1:
case TA_LEFT:
x = lpRect->left;
break;
case TA_RIGHT:
x = lpRect->right;
break;
case TA_CENTER:
x = (lpRect->right + lpRect->left) / 2;
break;
}
SetTextAlign(hDC, Align == -1 ? TA_LEFT : Align);

for(i = 0; i <= nCount; i++)
{
if(lpString[i] == ' ' || lpString[i] == '\t' || lpString[i] == '\0' || i == nCount || _strnicmp(lpString + i, "\r\n", 2) == 0)
{
cEnd = (char*)lpString + i;
Size.cx = (int)LOWORD(GetTabbedTextExtent(hDC, cLineStart, cEnd - cLineStart, 0, NULL));
w = Size.cx;

if(w > lpRect->right - lpRect->left)
{
Size.cx = (int)LOWORD(GetTabbedTextExtent(hDC, cLineStart, cStart - cLineStart, 0, NULL));

w = (lpRect->right - lpRect->left) - Size.cx;

if(Align == -1)SetTextJustification(hDC, w, j);
TabbedTextOut(hDC, x, y, cLineStart, cStart - cLineStart, 0, NULL, lpRect->left);
if(Align == -1)SetTextJustification(hDC, 0, 0);

y += h;

if(*cStart == ' ')
cStart++;
cLineStart = cStart;
j = 0;
}
else
{
if(lpString[i] == ' ')
j++;
cStart = cEnd;
}
}
if(lpString[i] == '\0' || i == nCount)
{
TabbedTextOut(hDC, x, y, cLineStart, i - (cLineStart - lpString), 0, NULL, lpRect->left);
}
if(_strnicmp(lpString + i, "\r\n", 2) == 0)
{
TabbedTextOut(hDC, x, y, cLineStart, i - (cLineStart - lpString), 0, NULL, lpRect->left);
cStart = cLineStart = (char*)lpString + i + 2;
y += h;
w = 0;
j = 0;
}
}
return y - lpRect->top;
}


As you will see (if you want to place it in your apps) lines with tabs after the middle will not get correctly justified, just like Word does. Anyway there's no clipping to the formatting rectangle, but I don't need it. Yes yes, it is very raw but it works for my needs.