Thanks for all this positive remarks but for some of them it may be true
but not as important as it seems.
let me know ...
takes too much space for what I need.
Joseph M. Newcomer a ?crit :
See below,,,
On Mon, 20 Aug 2007 15:37:16 +0200, mosfet <john.doe@anonymous.org> wrote:
Actually I want to use the following control
http://www.codeproject.com/listctrl/html_listctrl.asp (on Smartphone but
that's not the question) that is a Custom CListCtrl deriving from CWnd.
It's appropriate because on smartphone you don't have much space and
this listctrl allow to have a different size for each row in addtion to
support a few HTML tags.
But when reading the code I saw that it uses hfontBase =
(HFONT)SelectObject(hdc, (HFONT)GetStockObject(SYSTEM_FONT)); and I
think it would be better to use the font selected in the control.
For instanc I could do a SetFont on my listctrl and the DrawHTML
function should use it to do its rendering (bold, italic, ...)
About savedc and restordc it's because I haven't posted all the code.
int __stdcall DrawHTML(
HDC hdc, // handle of device context
LPCTSTR lpString, // address of string to draw
int nCount, // string length, in characters
LPRECT lpRect, // address of structure with
formatting dimensions
UINT uFormat // text-drawing flags
)
{
LPCTSTR Start;
int Left, Top, MaxWidth, MinWidth, Height;
****
You should avoid commas in declaration lists. It makes the code unreadable, and hard to
edit. One variable, one line should be the rule.
****
int SavedDC;
int Tag, TokenLength;
HFONT hfontBase, hfontSpecial[FV_NUMBER];
int Styles, CurStyles;
SIZE size;
int Index, LineHeight;
POINT CurPos;
int WidthOfSPace, XPos;
BOOL WhiteSpace;
RECT rc;
if (hdc == NULL || lpString == NULL)
return 0;
if (nCount < 0)
nCount = _tcslen(lpString);
if (lpRect != NULL) {
Left = lpRect->left;
Top = lpRect->top;
MaxWidth = lpRect->right - lpRect->left;
} else {
GetCurrentPositionEx(hdc, &CurPos);
Left = CurPos.x;
Top = CurPos.y;
MaxWidth = GetDeviceCaps(hdc, HORZRES) - Left;
****
Note that this is very sensitive to where the current position is; can you really trust it
to be in a sensible place?
****
} /* if */
****
It is good to indicate the end of nested structures, but this tends to break down if you
have a nested if. I tend to something stronger:
if(lpRect != NULL)
{ /* Use given rect */
... code here
} /* Use given rect */
else
{ /* Compute default rect */
... code here
} /* Compute default rect */
This deals with one of our weaknesses as human beings: we cannot cope well with nesting.
My editor, when I type {, puts in a comment and leaves me in the middle ready to type the
contents; when I type }, my editor grabs the comment from the matching { and duplicates
it, meaning I never, ever have to guess which } matches which {.
****
if (MaxWidth < 0)
MaxWidth = 0;
/* toggle flags we do not support */
****
Actually, you are not "toggling" them (which would imply setting them to the opposite of
their current value), you are "clearing" them. And the next line "sets" others.
****
uFormat &= ~(DT_CENTER | DT_RIGHT | DT_TABSTOP);
uFormat |= (DT_LEFT | DT_NOPREFIX);
/* get the "default" font from the DC */
SavedDC = SaveDC(hdc);
hfontBase = ::GetCurrentObject(hdc, OBJ_FONT);
hfontBase = (HFONT)SelectObject(hdc, (HFONT)GetStockObject(SYSTEM_FONT));
SelectObject(hdc, hfontBase);
***
But the above two lines with SelectObject is now meaningless!
Get rid of these two lines, since GetCurrentObject does what you need!
****
/* clear the other fonts, they are created "on demand" */
for (Index = 0; Index < FV_NUMBER; Index++)
hfontSpecial[Index] = NULL;
****
What if they weren't already NULL? If they already existed, you now have a handle leak.
They should be cleared when the fonts are deleted using ::DeleteObject, not here.
****
hfontSpecial[0] = hfontBase;
Styles = 0; /* assume the active font is normal weight, roman,
non-underlined */
/* get font height (use characters with ascender and descender);
* we make the assumption here that changing the font style will
* not change the font height
*/
GetTextExtentPoint32(hdc, _T("?y"), 2, &size);
LineHeight = size.cy;
****
GetTextMetrics would be a better choice here, since you can't really depend on how the
font represents ?. You would then use the tmHeight for line height, although you usually
get a better result from using tmHeight + tmInternalLeading. These will account for all
the ascender/descender values, not just the incidental representations of two characters
(which may not have ascenders and descenders in a particular font)
****
/* run through the string, word for word */
***
This code is a bit dangerous, because you really don't know what a "word" is (there is a
myth that words are separated by whitespace, which is only true in a limited number of
languages)
XPos = 0;
MinWidth = 0;
stacktop = 0;
CurStyles = -1; /* force a select of the proper style */
Height = 0;
WhiteSpace = FALSE;
Start = lpString;
for ( ;; ) {
Tag = GetToken(&Start, &nCount, &TokenLength, &WhiteSpace);
if (Tag < 0)
break;
switch (Tag & ~ENDFLAG) {
case tP:
****
Putting comments like <p> would make it a little easier to understand what is going on
here...
****
if ((Tag & ENDFLAG) == 0 && (uFormat & DT_SINGLELINE) == 0) {
if (Start != lpString)
Height += 3 * LineHeight / 2;
XPos = 0;
} /* if */
break;
case tBR:
if ((Tag & ENDFLAG) == 0 && (uFormat & DT_SINGLELINE) == 0) {
Height += LineHeight;
XPos = 0;
} /* if */
break;
case tB:
Styles = (Tag & ENDFLAG) ? Styles & ~FV_BOLD : Styles | FV_BOLD;
break;
case tI:
Styles = (Tag & ENDFLAG) ? Styles & ~FV_ITALIC : Styles | FV_ITALIC;
break;
case tU:
Styles = (Tag & ENDFLAG) ? Styles & ~FV_UNDERLINE : Styles |
FV_UNDERLINE;
break;
case tSUB:
Styles = (Tag & ENDFLAG) ? Styles & ~FV_SUBSCRIPT : Styles |
FV_SUBSCRIPT;
break;
case tSUP:
Styles = (Tag & ENDFLAG) ? Styles & ~FV_SUPERSCRIPT : Styles |
FV_SUPERSCRIPT;
break;
case tFONT:
if ((Tag & ENDFLAG) == 0) {
if (_tcsnicmp(Start + 6, _T("color="), 6) == 0)
PushColor(hdc, ParseColor(Start + 12));
****
This really makes a very strong assumption which is almost certainly not true, that the
specification is always
<font color="colorid">
but it could just as easily be
<font size="3" color="colorid">
so you really have to parse the parameters as an unordered set.
****
} else {
PopColor(hdc);
****
Actually, you need to pop the entire font context, not just the color, because a font
specification could change the size as well.
****
} /* if */
break;
default:
****
What about &-escapes such as <?
****
if (Tag == (tNONE | ENDFLAG))
break;
if (CurStyles != Styles) {
if (hfontSpecial[Styles] == NULL)
hfontSpecial[Styles] = GetFontVariant(hdc, hfontBase, Styles);
CurStyles = Styles;
SelectObject(hdc, hfontSpecial[Styles]);
/* get the width of a space character (for word spacing) */
GetTextExtentPoint32(hdc, _T(" "), 1, &size);
WidthOfSPace = size.cx;
} /* if */
/* check word length, check whether to wrap around */
GetTextExtentPoint32(hdc, Start, TokenLength, &size);
****
As I learned to my dismay, this isn't really reliable for languages other than English. It
doesn't work with certain accented characters even in English. This gets you into issues
such as proper handling of Unicode. You would be better off using DrawText, which should
do the correct font computations correctly. Generally, trying to do layout on your own is
very risky. Perhaps Mihai can jump in here; he's one of the Unicode layout experts. I
got into serious trouble trying to do word breaks in a client app, mostly because the
client gave me a set of "critical" specs that were incompatible with the reality of
international fonts, and it took a massive amount of effort to get them to change the
specs to recognize the fact that their specs wouldn't work.
****
if (size.cx > MaxWidth)
MaxWidth = size.cx; /* must increase width: long non-breakable word */
if (WhiteSpace)
XPos += WidthOfSPace;
if (XPos + size.cx > MaxWidth && WhiteSpace) {
if ((uFormat & DT_WORDBREAK) != 0) {
/* word wrap */
Height += LineHeight;
XPos = 0;
} else {
/* no word wrap, must increase the width */
MaxWidth = XPos + size.cx;
} /* if */
} /* if */
/* output text (unless DT_CALCRECT is set) */
if ((uFormat & DT_CALCRECT) == 0) {
/* handle negative heights, too (suggestion of "Sims") */
/*if (Top < 0)
{
SetRect(&rc, Left + XPos, Top - Height,
Left + MaxWidth, Top - (Height + LineHeight));
}
else*/
{
SetRect(&rc, Left + XPos, Top + Height,
Left + MaxWidth, Top + Height + LineHeight);
}
/* reposition subscript text to align below the baseline */
DrawText(hdc, Start, TokenLength, &rc,
uFormat | ((Styles & FV_SUBSCRIPT) ? DT_BOTTOM | DT_SINGLELINE : 0));
/* for the underline style, the spaces between words should be
* underlined as well
*/
if (WhiteSpace && (Styles & FV_UNDERLINE) && XPos >= WidthOfSPace)
{
if (Top < 0)
{
SetRect(&rc, Left + XPos - WidthOfSPace, Top - Height,
Left + XPos, Top - (Height + LineHeight));
}
else
{
SetRect(&rc, Left + XPos - WidthOfSPace, Top + Height,
Left + XPos, Top + Height + LineHeight);
}
DrawText(hdc, _T(" "), 1, &rc, uFormat);
} /* if */
} /* if */
/* update current position */
XPos += size.cx;
if (XPos > MinWidth)
MinWidth = XPos;
WhiteSpace = FALSE;
} /* if */
Start += TokenLength;
} /* for */
RestoreDC(hdc, SavedDC);
for (Index = 1; Index < FV_NUMBER; Index++) /* do not erase
hfontSpecial[0] */
if (hfontSpecial[Index] != NULL)
DeleteObject(hfontSpecial[Index]);
***
This is where you should set the handles to NULL. Not at the beginning.
Overall, there are a huge number of potential problems here as soon as you move outside
English, and the issue of height and word break needs to be treated in a more
culturally-neutral fashion.
***
/* store width and height back into the lpRect structure */
if ((uFormat & DT_CALCRECT) != 0 && lpRect!=NULL) {
lpRect->right = lpRect->left + MinWidth;
if (lpRect->top < 0)
lpRect->bottom = lpRect->top - (Height + LineHeight);
else
lpRect->bottom = lpRect->top + Height + LineHeight;
} /* if */
return Height;
}
Joseph M. Newcomer a ?crit :
On Mon, 20 Aug 2007 15:05:43 +0200, mosfet <john.doe@anonymous.org> wrote:
Hi,
I would like to modify a function used to render some HTML and use the
font I have choosen :
// C functions
int __stdcall DrawHTML(
HDC hdc, // handle of device context
LPCTSTR lpString, // address of string to draw
int nCount, // string length, in characters
LPRECT lpRect, // address of structure with
formatting dimensions
UINT uFormat // text-drawing flags
)
{
...
/* get the "default" font from the DC */
SavedDC = SaveDC(hdc);
****
You do a SaveDC but not a RestoreDC?
****
hfontBase = (HFONT)SelectObject(hdc, (HFONT)GetStockObject(SYSTEM_FONT));
SelectObject(hdc, hfontBase);
****
It would be easier to write
hfontBase = ::GetCurrentObject(hdc, OBJ_FONT);
and not worry about the SelectObject pair
****
/* clear the other fonts, they are created "on demand" */
for (Index = 0; Index < FV_NUMBER; Index++)
hfontSpecial[Index] = NULL;
hfontSpecial[0] = hfontBase;
****
Note that if, in between times, the font which is currently selected into the DC is
deleted, the old handle you have saved here will become meaningless. Therefore, you
cannot rely on the fact that this handle will still be valid at the time you use it.
Better to do an array of LOGFONT values, rather than an array of HFONTs.
****
...
}
Here is my code :
void CxListCtrl::DrawItem(CDC *pDC, CRect rcItem, HTMLLIST_ITEM *pItem,
BOOL bSelected)
{
CFont* pOldFont = MemDC.SelectObject( &m_font );
DrawHTML(pDC->GetSafeHdc(),pItem->sItemText,pItem->sItemText.GetLength(),
&rc,DT_LEFT|DT_WORDBREAK);
****
So why do you save the font in the MemDC when you don't actually use it? Note that the
m_font handle must represent a valid font to be selected, but the line seems to have no
meaning as shown in this function. I would also suggest something like
HDC dc = pDC->GetSafeHdc();
if(dc == NULL)
return;
DrawHTML(dc, ...as above...);
}
My problem is I don't know how to modify DrawHTML to take the font
specified in DrawItem because from what I understand DrawHTML is using a
default font :
hfontBase = (HFONT)SelectObject(hdc, (HFONT)GetStockObject(SYSTEM_FONT));
SelectObject(hdc, hfontBase);
****
I don't see the relationship of the above code to the problem. Where is this line of
code? I also could not find any method called DrawHTML in the MSDN documentation, could
you say more about where it is implemented or what it is supposed to do?
joe
****
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm