Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

Easy Profiler - Compile-time Profiler for C++

, 14 Nov 2009 Apache
Easily instrument your code, visualize, interpret results, track optimization, compare and decide.
Deploy.zip
Collector
Collector.dll
Collector.lib
Observer
Chart Provider.dll
CLRAdapter.dll
Collector.dll
Observer.exe
Misc.zip
Easy Profiler.chm
Ressources
analyze.ico
analyzeExport16.ico
Book1.xlsx
ChartDesign.ico
chartDesign16.ico
ChartFormat16.ico
compare.ico
compare16.ico
Export.ico
Export16.ico
fonts.ico
front.png
Home.ico
Outlook.ico
Project1.ico
SmallButtons.ico
Support.ico
Support16.ico
TREE.ico
Source.zip
Chart Provider
app.ico
Chart Provider.vcproj.STANDARD.Administrateur.user
Release
CLRAdapter
CLRAdapter.def
CLRAdapter.vcproj.STANDARD.Administrateur.user
Release
res
Profiler.vcproj.STANDARD.Administrateur.user
Release
Back
Observer.vcproj.STANDARD.Administrateur.user
Release
res
analyze.bmp
analyzeExport16.bmp
buttons.bmp
ChartDesign.bmp
chartDesign16.bmp
ChartDesignLarge.bmp
ChartFormat.bmp
ChartFormatLarge.bmp
ChartLayout.bmp
ChartLayoutLarge.bmp
ChartMenu.bmp
compare.bmp
Copie de main.bmp
Export.bmp
Export16.bmp
filelarge.bmp
filesmall.bmp
main.bmp
nav_large.bmp
nav_large_hc.bmp
Observer.ico
ObserverDoc.ico
pages.bmp
pages_hc.bmp
pages_small.bmp
pages_small_hc.bmp
Project3.ico
Project4.ico
Project6.ico
Support.bmp
Support16.bmp
Toolbar.bmp
Toolbar256.bmp
treeSmall.bmp
WriteFont.bmp
writelarge.bmp
writesmall.bmp
TipOfTheDay
xHTML
Output
Chart Provider.dll
CLRAdapter.dll
CLRAdapter.lib
Collector.dll
Collector.lib
Observer.exe
Setup
Setup.vdproj
// XHtmlDraw.cpp  Version 1.2 - article available at www.codeproject.com
//
// Author:  Hans Dietrich
//          hdietrich@gmail.com
//
// History
//     Version 1.2 - 2007 November 6
//     - Fixed problem where malformed HTML could cause infinite loop, 
//       reported by bolivar123.
//     - Removed unnecessary calls to CRT; increased performance by about 25%.
//
//     Version 1.1 - 2007 August 15
//     - Minor enhancements
//     - Improved performance of GetPlainText() when no html in string
//     - added copy and assignment ctors to XHTMLDRAWSTRUCT
//
//     Version 1.0 - 2007 July 15
//     - Initial public release
//
// License:
//     This software is released into the public domain.  You are free to use
//     it in any way you like, except that you may not sell this source code.
//
//     This software is provided "as is" with no expressed or implied warranty.
//     I accept no liability for any damage or loss of business that this 
//     software may cause.
//
///////////////////////////////////////////////////////////////////////////////

// NOTE ABOUT PRECOMPILED HEADERS:
// This file does not need to be compiled with precompiled headers (.pch).
// To disable this, go to Project | Settings | C/C++ | Precompiled Headers
// and select "Not using precompiled headers".  Be sure to do this for all
// build configurations.
//#include "stdafx.h"

#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
#include "XNamedColors.h"
#include "XString.h"
#include "XHtmlDraw.h"

#ifndef __noop
#if _MSC_VER < 1300
#define __noop ((void)0)
#endif
#endif

#undef TRACE
#define TRACE __noop

// if you want to see the TRACE output, 
// uncomment this line:
//#include "XTrace.h"

#pragma warning(disable : 4127)	// for _ASSERTE: conditional expression is constant
#pragma warning(disable : 4996)	// disable bogus deprecation warning


///////////////////////////////////////////////////////////////////////////////
//
// HTML CHARACTER ENTITIES
//
// Some common character entities - note that these will be displayed correctly
// ONLY if they are in the currently selected font
//
CXHtmlDraw::CHAR_ENTITIES CXHtmlDraw::m_aCharEntities[] = 
{
	{ _T("&amp;"),		0,	_T('&') },		// ampersand
	{ _T("&bull;"),		0,	_T('\x95') },	// bullet      NOT IN MS SANS SERIF
	{ _T("&cent;"),		0,	_T('\xA2') },	// cent sign
	{ _T("&copy;"),		0,	_T('\xA9') },	// copyright
	{ _T("&deg;"),		0,	_T('\xB0') },	// degree sign
	{ _T("&euro;"),		0,	_T('\x80') },	// euro sign
	{ _T("&frac12;"),	0,	_T('\xBD') },	// fraction one half
	{ _T("&frac14;"),	0,	_T('\xBC') },	// fraction one quarter
	{ _T("&gt;"),		0,	_T('>') },		// greater than
	{ _T("&iquest;"),	0,	_T('\xBF') },	// inverted question mark
	{ _T("&lt;"),		0,	_T('<') },		// less than
	{ _T("&micro;"),	0,	_T('\xB5') },	// micro sign
	{ _T("&middot;"),	0,	_T('\xB7') },	// middle dot = Georgian comma
	{ _T("&nbsp;"),		0,	_T(' ') },		// nonbreaking space
	{ _T("&para;"),		0,	_T('\xB6') },	// pilcrow sign = paragraph sign
	{ _T("&plusmn;"),	0,	_T('\xB1') },	// plus-minus sign
	{ _T("&pound;"),	0,	_T('\xA3') },	// pound sign
	{ _T("&quot;"),		0,	_T('"') },		// quotation mark
	{ _T("&reg;"),		0,	_T('\xAE') },	// registered trademark
	{ _T("&sect;"),		0,	_T('\xA7') },	// section sign
	{ _T("&sup1;"),		0,	_T('\xB9') },	// superscript one
	{ _T("&sup2;"),		0,	_T('\xB2') },	// superscript two
	{ _T("&times;"),	0,	_T('\xD7') },	// multiplication sign
	{ _T("&trade;"),	0,	_T('\x99') },	// trademark   NOT IN MS SANS SERIF
	{ NULL,				0,	0 }				// MUST BE LAST
};


///////////////////////////////////////////////////////////////////////////////
// ctor
CXHtmlDraw::CXHtmlDraw(UINT nMaxText /*= XHTMLDRAW_MAX_TEXT*/)
  : m_nMaxText(nMaxText),
	m_bOverAnchor(FALSE)
{
	InitCharEntities();
}

///////////////////////////////////////////////////////////////////////////////
// dtor
CXHtmlDraw::~CXHtmlDraw()
{

}

///////////////////////////////////////////////////////////////////////////////
// Draw
int CXHtmlDraw::Draw(HDC hDC, 
					 LPCTSTR lpszText, 
					 XHTMLDRAWSTRUCT * pXHDS, 
					 BOOL bUnderlineUrl)
{
	TRACE(_T("in CXHtmlDraw::Draw:  <%s>  bUnderlineUrl=%d\n"), lpszText, bUnderlineUrl);

	static BOOL bInDraw = FALSE;

	if (bInDraw)
		return 0;
	bInDraw = TRUE;

	// check parameters ----------------------------------------------

	_ASSERTE(hDC);
	_ASSERTE(lpszText);
	_ASSERTE(pXHDS);
	if (!hDC || !lpszText || (lpszText[0] == _T('\0')) || !pXHDS)
	{
		bInDraw = FALSE;
		return 0;
	}

	HWND hWnd = ::WindowFromDC(hDC);

	if (!::IsWindow(hWnd))
	{
		TRACE(_T("warn: not a window\n"));
		bInDraw = FALSE;
		return 0;
	}

	// check if window is hidden ------------------------------------

	if (!::IsWindowVisible(hWnd))
	{
		TRACE(_T("warn: window invisible\n"));
		bInDraw = FALSE;
		return 0;
	}

	RECT rectClip;
	int nResult = ::GetClipBox(hDC, &rectClip);

	if (nResult == NULLREGION)
	{
		//	window is covered
		TRACE(_T("warn: window is covered\n"));
		bInDraw = FALSE;
		return 0;
	}

	// initialize for drawing ---------------------------------------

	// rectText is used to draw into the dc
	RECT rectText = pXHDS->rect;
	if ((rectText.left >=  rectText.right) || 
		(rectText.top >= rectText.bottom))
	{
		TRACE(_T("warn: bad rect\n"));
		bInDraw = FALSE;
		return 0;
	}

	// rectDraw is the rect for the entire drawing area
	RECT rectDraw = pXHDS->rect;
	int nRectWidth  = rectDraw.right - rectDraw.left;
	int nRectHeight = rectDraw.bottom - rectDraw.top;
	int nXOffset = rectDraw.left;

	// set up for double buffering
	HDC hMemDC = CreateCompatibleDC(hDC);
	HBITMAP hBitmap = CreateCompatibleBitmap(hDC, nRectWidth, nRectHeight);
	HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemDC, hBitmap);

	if (pXHDS->bTransparent && !pXHDS->hDC)
	{
		// save a bitmap of the original drawing area, in case
		// there are links, and we need to erase the underline
		pXHDS->hDC = CreateCompatibleDC(hDC);
		pXHDS->hBitmap = CreateCompatibleBitmap(hDC, nRectWidth, nRectHeight);
		pXHDS->hOldBitmap = (HBITMAP) SelectObject(pXHDS->hDC, pXHDS->hBitmap);
		BitBlt(pXHDS->hDC, 0, 0, nRectWidth, nRectHeight,
			hDC, rectDraw.left, rectDraw.top, SRCCOPY);
		BitBlt(hMemDC, 0, 0, nRectWidth, nRectHeight,
			hDC, rectDraw.left, rectDraw.top, SRCCOPY);
	}
	else if (pXHDS->bTransparent && pXHDS->hDC)
	{
		// restore the original drawing area from saved HDC
		BitBlt(hMemDC, 0, 0, nRectWidth, nRectHeight,
			pXHDS->hDC, 0, 0, SRCCOPY);
	}

	pXHDS->rectAnchor = rectText;	// save rect in case of anchor

	// remap rectText to memory dc - left and top start at 0
	rectText.left = 0;
	rectText.top = 0;
	rectText.right = nRectWidth;
	rectText.bottom = nRectHeight;

	// create initial font ------------------------------------------

	LOGFONT lf = { 0 };
	LOGFONT prev_lf = { 0 };

	if (pXHDS->bLogFont)
	{
		TRACE(_T("using logfont\n"));
		memcpy(&lf, &pXHDS->lf, sizeof(LOGFONT));
	}
	else
	{
		HFONT hfont = (HFONT)::GetCurrentObject(hDC, OBJ_FONT);	//+++1.1
		if (hfont)
			GetObject(hfont, sizeof(LOGFONT), &lf);
		else
			GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &lf);
	}
	memcpy(&prev_lf, &lf, sizeof(LOGFONT));

	// variable initialization --------------------------------------

	TCHAR *pszText = new TCHAR [m_nMaxText+1];
	memset(pszText, 0, (m_nMaxText+1)*sizeof(TCHAR));
	_tcsncpy(pszText, lpszText, m_nMaxText);
	TCHAR *pTextBuffer = pszText;	// save buffer address for delete

	TCHAR *pszText1 = new TCHAR [m_nMaxText+1];
	memset(pszText1, 0, (m_nMaxText+1)*sizeof(TCHAR));

	if (pXHDS->pszAnchor)
		delete [] pXHDS->pszAnchor;
	pXHDS->pszAnchor = NULL;

	pXHDS->bHasAnchor = FALSE;
	pXHDS->bAnchorIsUnderlined = FALSE;
	BOOL bInAnchor = FALSE;

	int n = (int) _tcslen(pszText);		// n must be int

	int i = 0;
	int nWidth = 0;

	pXHDS->bHasAnchor = FALSE;
	pXHDS->nRightX = 0;
	
	COLORREF crText = pXHDS->crText;
	if (crText == COLOR_NONE)
		crText = GetSysColor(COLOR_WINDOWTEXT);

	COLORREF crBackground = pXHDS->crBackground;
	if (crBackground == COLOR_NONE)
		crBackground = GetSysColor(COLOR_WINDOW);

	COLORREF crTextNew = crText;
	COLORREF crBkgndNew = crBackground;

	// if no transparency, fill entire rect with default bg color
	if (!pXHDS->bTransparent)
	{
		HBRUSH hbrush = CreateSolidBrush(crBkgndNew); 
		_ASSERTE(hbrush);
		FillRect(hMemDC, &rectText, hbrush);
		if (hbrush)
			DeleteObject(hbrush);
	}

	BOOL bBold = pXHDS->bBold;
	BOOL bItalic = pXHDS->bItalic;
	BOOL bUnderline = pXHDS->bUnderline;
	BOOL bStrikeThrough = pXHDS->bStrikeThrough;
	BOOL bSubscript = FALSE;
	BOOL bSuperscript = FALSE;
	int nSizeChange = 0;

	// replace character entity names in text with codes ------------

	TCHAR ent[3] = { _T('\0') };
	ent[0] = _T('\001');	// each entity name is replaced with a two-character
							// code that begins with \001

	BOOL bCharacterEntities = FALSE;	// assume no char entities

	// we are replacing character entites with a two-character sequence,
	// so the resulting string will be shorter
	size_t buflen = _tcslen(pszText) + 100;
	TCHAR *buf = new TCHAR [buflen];
	memset(buf, 0, buflen*sizeof(TCHAR));

	for (i = 0; m_aCharEntities[i].pszName != NULL; i++)
	{
		ent[1] = m_aCharEntities[i].cCode;
		int nRep = _tcsistrrep(pszText, m_aCharEntities[i].pszName, ent, buf);
		if (nRep > 0)
		{
			bCharacterEntities = TRUE;
			_tcscpy(pszText, buf);
		}
	}

	delete [] buf;
	buf = NULL;

	TEXTMETRIC tm = { 0 };

	n = (int) _tcslen(pszText);	// get length again after char entity substitution
	int textLen = n;

	while ((n > 0) && pszText && (pszText < (pTextBuffer + textLen)))
	{
		TRACE(_T("start while:  n=%d  pszText=<%s>\n"), n, pszText);

		///////////////////////////////////////////////////////////////////////
		if (_tcsnicmp(pszText, _T("<B>"), 3) == 0)	// check for <b> or <B>
		{
			n -= 3;
			pszText += 3;
			bBold++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</B>"), 4) == 0)	// check for </B>
		{
			n -= 4;
			pszText += 4;
			if (bBold)
				bBold--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<I>"), 3) == 0)	// check for <I>
		{
			n -= 3;
			pszText += 3;
			bItalic++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</I>"), 4) == 0)	// check for </I>
		{
			n -= 4;
			pszText += 4;
			if (bItalic)
				bItalic--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<U>"), 3) == 0)		// check for <U>
		{
			n -= 3;
			pszText += 3;
			bUnderline++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</U>"), 4) == 0)	// check for </U>
		{
			n -= 4;
			pszText += 4;
			if (bUnderline)
				bUnderline--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<S>"), 3) == 0)		// check for <S>
		{
			n -= 3;
			pszText += 3;
			bStrikeThrough++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</S>"), 4) == 0)	// check for </S>
		{
			n -= 4;
			pszText += 4;
			if (bStrikeThrough)
				bStrikeThrough--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<BIG>"), 5) == 0)	// check for <BIG>
		{
			n -= 5;
			pszText += 5;
			if (lf.lfHeight > 0)
				lf.lfHeight += 2;
			else
				lf.lfHeight -= 2;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</BIG>"), 6) == 0)	// check for </BIG>
		{
			n -= 6;
			pszText += 6;
			if (lf.lfHeight > 0)
				lf.lfHeight -= 2;
			else
				lf.lfHeight += 2;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<SMALL>"), 7) == 0)	// check for <SMALL>
		{
			n -= 7;
			pszText += 7;
			if (lf.lfHeight > 0)
				lf.lfHeight -= 2;
			else
				lf.lfHeight += 2;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</SMALL>"), 8) == 0)	// check for </SMALL>
		{
			n -= 8;
			pszText += 8;
			if (lf.lfHeight > 0)
				lf.lfHeight += 2;
			else
				lf.lfHeight -= 2;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<SUB>"), 5) == 0)	// check for <SUB>
		{
			n -= 5;
			pszText += 5;
			bSubscript++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</SUB>"), 6) == 0)	// check for </SUB>
		{
			n -= 6;
			pszText += 6;
			if (bSubscript)
				bSubscript--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<SUP>"), 5) == 0)	// check for <SUP>
		{
			n -= 5;
			pszText += 5;
			bSuperscript++;// = TRUE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</SUP>"), 6) == 0)	// check for </SUP>
		{
			n -= 6;
			pszText += 6;
			if (bSuperscript)
				bSuperscript--;// = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<FONT"), 5) == 0)	// check for <FONT
		{
			TRACE(_T("found font\n"));
			TCHAR *cp = _tcschr(pszText, _T('>'));
			if (cp)
			{
				TCHAR szAttributes[XHTMLDRAW_MAX_TEXT] = { 0 };
				_tcsncpy(szAttributes, &pszText[5], cp-pszText-5);
				TRACE(_T("szAttributes=<%s>\n"), szAttributes);
				size_t m = _tcslen(szAttributes);
				n -= (int) (cp - pszText + 1);
				pszText = cp + 1;

				// loop to parse FONT attributes
				while (m > 0)
				{
					// trim left whitespace
					if ((_tcslen(szAttributes) > 0) && 
						(szAttributes[0] == _T(' ')))
					{
						m--;
						_tcscpy(szAttributes, &szAttributes[1]);
						continue;
					}

					///////////////////////////////////////////////////////////
					if (_tcsnicmp(szAttributes, _T("COLOR"), 5) == 0)
					{
						TRACE(_T("found color\n"));
						TCHAR *cp2 = _tcschr(szAttributes, _T('"'));
						if (cp2)
						{
							m -= (cp2 - szAttributes) + 1;
							_tcscpy(szAttributes, cp2+1);

							cp2 = _tcschr(szAttributes, _T('"'));
							if (cp2)
							{
								*cp2 = _T('\0');
								TCHAR szColor[XHTMLDRAW_MAX_TEXT] = { _T('\0') };
								_tcsncpy(szColor, szAttributes, cp2-szAttributes+1);
								TRACE(_T("szColor=<%s>\n"), szColor);
								CXNamedColors nc(szColor);
								if (!pXHDS->bIgnoreColorTag)
									crTextNew = nc.GetRGB();
								_tcscpy(szAttributes, cp2+1);
								m = _tcslen(szAttributes);
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (_tcsnicmp(szAttributes, _T("BGCOLOR"), 7) == 0)
					{
						TRACE(_T("found bgcolor\n"));
						TCHAR *cp2 = _tcschr(szAttributes, _T('"'));
						if (cp2)
						{
							m -= cp2 - szAttributes + 1;
							_tcscpy(szAttributes, cp2+1);

							cp2 = _tcschr(szAttributes, _T('"'));
							if (cp2)
							{
								*cp2 = _T('\0');
								TCHAR szBgColor[XHTMLDRAW_MAX_TEXT] = { _T('\0') };
								_tcsncpy(szBgColor, szAttributes, cp2-szAttributes+1);
								TRACE(_T("szBgColor=<%s>\n"), szBgColor);
								CXNamedColors nc(szBgColor);
								crBkgndNew = nc.GetRGB();
								_tcscpy(szAttributes,  cp2+1);
								m = _tcslen(szAttributes);
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (_tcsnicmp(szAttributes, _T("FACE"), 4) == 0)
					{
						TCHAR *cp2 = _tcschr(szAttributes, _T('"'));
						if (cp2)
						{
							m -= cp2 - szAttributes + 1;
							_tcscpy(szAttributes, cp2+1);
							cp2 = _tcschr(szAttributes, _T('"'));
							if (cp2)
							{
								const int nFaceSize = sizeof(lf.lfFaceName);	// in bytes
								int nMaxFaceSize = nFaceSize / sizeof(TCHAR);	// in TCHARs
								memset(lf.lfFaceName, 0, nFaceSize);
								int nNewFaceSize = (int)(cp2 - szAttributes);	// in TCHARs
								memset(&lf.lfFaceName, 0, nFaceSize);
								_tcsncpy(lf.lfFaceName, szAttributes, 
									(nNewFaceSize > nMaxFaceSize) ? nMaxFaceSize : nNewFaceSize);
								TRACE(_T("lf.lfFaceName=<%s>\n"), lf.lfFaceName);

								m -= cp2 - szAttributes + 1;
								if (m > 0)
									_tcscpy(szAttributes, cp2+1);
								else
									szAttributes[0] = _T('\0');
								m = _tcslen(szAttributes);
							}
						}
						else
							break;
					}
					///////////////////////////////////////////////////////////
					else if (_tcsnicmp(szAttributes, _T("SIZE"), 4) == 0)
					{
						TCHAR *cp2 = _tcschr(szAttributes, _T('"'));
						if (cp2)
						{
							m -= cp2 - szAttributes + 1;
							_tcscpy(szAttributes, cp2+1);
							cp2 = _tcschr(szAttributes, _T('"'));
							if (cp2)
							{
								int nSize = _ttoi(szAttributes);
								lf.lfHeight -= nSize;
								nSizeChange = nSize;

								m -= cp2 - szAttributes + 1;
								if (m > 0)
									_tcscpy(szAttributes, cp2+1);
								else
									szAttributes[0] = _T('\0');
								m = _tcslen(szAttributes);
							}
						}
						else
							break;
					}
					else
					{
						while ((_tcslen(szAttributes) > 0) && 
							   (szAttributes[0] != _T(' ')))
						{
							m--;
							_tcscpy(szAttributes, &szAttributes[1]);
						}
					}
				}
			}
			else
			{
				TRACE(_T("ERROR no closing >\n"));
				pszText += 5;
				n -= 5;
			}
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</FONT>"), 7) == 0)	// check for </FONT>
		{
			n -= 7;
			pszText += 7;
			crTextNew = crText;
			crBkgndNew = crBackground;
			memcpy(&lf, &prev_lf, sizeof(lf));
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("<A"), 2) == 0)	// check for <A
		{
			TCHAR *cp = 0;
			if ((cp = _tcsistr(pszText, _T("HREF="))) != NULL)	// check for HREF=
			{
				cp += 5;
				if (*cp == _T('"'))
					cp += 1;
				TCHAR *cp2 = _tcschr(cp, _T('>'));
				if (cp2)
				{
					size_t len = cp2 - cp;
					if (pXHDS->pszAnchor)
						delete [] pXHDS->pszAnchor;
					pXHDS->pszAnchor = new TCHAR [len+4];
					memset(pXHDS->pszAnchor, 0, (len+4)*sizeof(TCHAR));
					_tcsncpy(pXHDS->pszAnchor, cp, len);
					size_t last = _tcslen(pXHDS->pszAnchor);
					if (last > 0)
						last--;
					if (pXHDS->pszAnchor[last] == _T('"'))
						pXHDS->pszAnchor[last] = _T('\0');
					TRACE(_T("len=%d  pXHDS->szUrl=<%s>\n"), len, pXHDS->pszAnchor);
					n -= (int) (cp2 + 1 - pszText);
					pszText = cp2 + 1;
					TRACE(_T("pszText=<%s>\n"), pszText);

					// set start X of url
					pXHDS->rectAnchor.left = rectText.left + nXOffset;
					TRACE(_T("setting pXHDS->rectAnchor.left to %d\n"), pXHDS->rectAnchor.left);

					crTextNew = pXHDS->crAnchorText; //RGB(0,0,255);	//pXHDS->crText;
					crBkgndNew = crBackground;
					memcpy(&lf, &prev_lf, sizeof(lf));

					bInAnchor = TRUE;

					if (bUnderlineUrl)
					{
						pXHDS->bAnchorIsUnderlined = TRUE;
						bUnderline++;
					}
				}
			}
			else
			{
				TRACE(_T("ERROR no closing >\n"));
				pszText += 2;
				n -= 2;
			}
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		else if (_tcsnicmp(pszText, _T("</A>"), 4) == 0)	// check for </A>
		{
			n -= 4;
			pszText += 4;

			if (bInAnchor)
			{
				pXHDS->rectAnchor.right = rectText.left + nXOffset;
				pXHDS->bHasAnchor = TRUE;
				TRACE(_T("setting pXHDS->rectAnchor.right to %d\n"), pXHDS->rectAnchor.right);

				if (bUnderlineUrl)
					bUnderline--;
				crTextNew = crText;
				crBkgndNew = crBackground;
				memcpy(&lf, &prev_lf, sizeof(lf));
			}

			bInAnchor = FALSE;
			continue;
		}
		///////////////////////////////////////////////////////////////////////
		// plain text
		else
		{
			TRACE(_T("text:  pszText=<%s>\n"), pszText);
			TCHAR *cp = _tcschr(pszText, _T('<'));
			if (cp)
			{
				// there's another tag
				_tcsncpy(pszText1, pszText, cp - pszText);
				pszText1[cp-pszText] = _T('\0');
				TRACE(_T("pszText1=<%s>\n"), pszText1);

				if (_tcslen(pszText1) <= 0)
				{
					if (_tcslen(pszText) != 0)
					{
						_tcscpy(pszText1, pszText);
						n -= 1;
					}
				}
				pszText = cp;
			}
			else
			{
				// no more html tags
				_tcscpy(pszText1, pszText);
				pszText = NULL;
			}
		}
		TRACE(_T("pszText=<%s>\n"), pszText);
		TRACE(_T("pszText1=<%s>\n"), pszText1);

		// create new font ------------------------------------------

		lf.lfWeight    = bBold ? FW_BOLD : FW_NORMAL;
		lf.lfUnderline = (BYTE) bUnderline;
		lf.lfItalic    = (BYTE) bItalic;
		lf.lfStrikeOut = (BYTE) bStrikeThrough;

		HFONT hNewFont = CreateFontIndirect(&lf);
		_ASSERTE(hNewFont);

		HFONT hOldFont = (HFONT) SelectObject(hMemDC, hNewFont);

		SetTextColor(hMemDC, crTextNew);
		if (pXHDS->crTextBackground != COLOR_NONE)
			SetBkColor(hMemDC, pXHDS->crTextBackground);
		else
			SetBkMode(hMemDC, TRANSPARENT);		// need transparency for italic fonts

		// replace char entities ------------------------------------

		size_t end = _tcslen(pszText1);
		buflen = end + 100;
		_ASSERTE(buf == NULL);
		buf = new TCHAR [buflen];
		memset(buf, 0, buflen*sizeof(TCHAR));

		_tcsncpy(buf, pszText1, buflen-1);

		ReplaceCharEntities(buf, end);

		int len = (int)_tcslen(buf);
		SIZE size;
		GetTextExtentPoint32(hMemDC, buf, len, &size);
		LONG width = size.cx;

		if ((crBkgndNew != crBackground) &&
			(pXHDS->crTextBackground == COLOR_NONE))
		{
			// changing backgrounds, so fill in with new color
			HBRUSH hbrushnew = CreateSolidBrush(crBkgndNew); 
			if (hbrushnew)
			{
				RECT rect = rectText;
				rect.right = rect.left + width + 1;
				if (bItalic)
				{
					rect.right += 1;		// italic needs a little more
					if (!IsTrueType(hMemDC))
						rect.right += 2;	// non-TTF fonts need even more
				}
				if (rect.right > rectText.right)
					rect.right = rectText.right;
				FillRect(hMemDC, &rect, hbrushnew);
				DeleteObject(hbrushnew);
			}
		}

		UINT uFormat = pXHDS->uFormat;

		if (pXHDS->bUseEllipsis)
			uFormat |= DT_END_ELLIPSIS;

		if (pXHDS->bHasAnchor)
		{
			// set rect for anchor
			RECT rectCalc = rectText;
			int nHeight = DrawText(hMemDC, buf, -1, &rectCalc, uFormat | DT_CALCRECT);
			TRACE(_T("nHeight=%d -----\n"), nHeight);
			pXHDS->rectAnchor.bottom = pXHDS->rectAnchor.top + nHeight;
		}

		RECT savedrect = rectText;

		GetTextMetrics(hMemDC, &tm);
		int nBaselineAdjust = tm.tmAscent / 2;

		if (bSubscript)
		{
			rectText.top += nBaselineAdjust;
			rectText.bottom += nBaselineAdjust;
		}
		if (bSuperscript)
		{
			rectText.top -= nBaselineAdjust;
			rectText.bottom -= nBaselineAdjust;
		}

		// draw text ------------------------------------------------

		TRACE(_T("DrawText: <%s>\n"), buf);
		DrawText(hMemDC, buf, -1, &rectText, uFormat);

		rectText = savedrect;

		nSizeChange = 0;

		if (hOldFont)
			SelectObject(hMemDC, hOldFont);
		if (hNewFont)
			DeleteObject(hNewFont);
		hNewFont = 0;
		hOldFont = 0;

		delete [] buf;
		buf = NULL;

		rectText.left += width;

		n -= (int)_tcslen(pszText1);

	}	// while

	// save the rightmost pixel position - note that rectText
	// is remapped to 0,0 for the memory dc
	pXHDS->nRightX = rectText.left + nXOffset;
	TRACE(_T("nRightX = %d =====\n"), pXHDS->nRightX);

	// end double buffering
	BitBlt(hDC, rectDraw.left, rectDraw.top, nRectWidth, nRectHeight,
		hMemDC, 0, 0, SRCCOPY);			
	
	// swap back the original bitmap
	if (hOldBitmap)
		SelectObject(hMemDC, hOldBitmap);
	if (hBitmap)
		DeleteObject(hBitmap);
	hBitmap = 0;

	DeleteDC(hMemDC);
	hMemDC = 0;

	if (pTextBuffer)
		delete [] pTextBuffer;
	pTextBuffer = 0;
	if (pszText1)
		delete [] pszText1;
	pszText1 = 0;

	bInDraw = FALSE;

	return nWidth;
}

///////////////////////////////////////////////////////////////////////////////
// IsTrueType
BOOL CXHtmlDraw::IsTrueType(HDC hDC)
{
	BOOL rc = FALSE;

	_ASSERTE(hDC);

	TEXTMETRIC tm;
	if (hDC && GetTextMetrics(hDC, &tm))
		rc = tm.tmPitchAndFamily & TMPF_TRUETYPE;

	return rc;
}

///////////////////////////////////////////////////////////////////////////////
// InitCharEntities
void CXHtmlDraw::InitCharEntities()
{
	for (int i = 0; m_aCharEntities[i].pszName != NULL; i++)
	{
		m_aCharEntities[i].cCode = (TCHAR) (i + 2);	// don't use 0 or 1
	}
}

///////////////////////////////////////////////////////////////////////////////
// ReplaceCharEntities
void CXHtmlDraw::ReplaceCharEntities(TCHAR * buf, size_t buflen)
{
	_ASSERTE(buf);

	if (buf)
	{
		if (_tcschr(buf, _T('\001')))
		{
			TCHAR *cp1 = buf;
			TCHAR *cp2 = buf;

			while (cp1 < (buf + buflen))
			{
				TCHAR c = *cp1++;

				if (c == _T('\0'))
					break;

				if (c == _T('\001'))	// is this a char entity?
				{
					// get next char - it is table index
					c = *cp1++;

					if (c == _T('\0'))
						break;

					c = GetCharEntity(c);
				}

				*cp2++ = c;
			}
			*cp2 = _T('\0');
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// GetCharEntity
TCHAR CXHtmlDraw::GetCharEntity(TCHAR cCode)
{
	TCHAR c = _T(' ');

	for (int i = 0; m_aCharEntities[i].pszName != NULL; i++)
	{
		if (cCode == m_aCharEntities[i].cCode)
		{
			c = m_aCharEntities[i].cSymbol;
			break;
		}
	}

	return c;
}

///////////////////////////////////////////////////////////////////////////////
// GetPlainText
int CXHtmlDraw::GetPlainText(const TCHAR *html, 
							 TCHAR *plain, 
							 DWORD dwPlainSize)
{
	size_t nSize = 0;

	_ASSERTE(html);

	if (html)
	{
		if (plain)
			memset(plain, 0, dwPlainSize*sizeof(TCHAR));

		if (_tcschr(html, _T('<')) || _tcschr(html, _T('&')))
		{
			// there is html in this string

			// we are stripping out character entities, so the
			// resulting string will be shorter than the original
			size_t buflen = _tcslen(html) + 100;

			TCHAR *buf1 = new TCHAR [buflen];
			TCHAR *buf2 = new TCHAR [buflen];

			memset(buf1, 0, buflen*sizeof(TCHAR));
			memset(buf2, 0, buflen*sizeof(TCHAR));

			_tcsncpy(buf1, html, buflen-1);

			TCHAR ent[2] = { _T('\0') };

			// loop to remove character entities
			for (int i = 0; m_aCharEntities[i].pszName != NULL; i++)
			{
				ent[0] = m_aCharEntities[i].cSymbol;
				int nRep = _tcsistrrep(buf1, m_aCharEntities[i].pszName, ent, buf2);
				if (nRep > 0)
					_tcscpy(buf1, buf2);
			}

			TRACE(_T("after entities:  <%s>\n"), buf1);

			TCHAR *cp1 = buf1;
			TCHAR *cp2 = NULL;
			nSize = _tcslen(buf1);
			memset(buf2, 0, buflen*sizeof(TCHAR));

			while (cp1 < (buf1 + nSize))
			{
				if (!*cp1)
					break;

				// look for html tag
				cp2 = _tcschr(cp1, _T('<'));

				if (cp2)
				{
					// copy everything up to this point
					_tcsncat(buf2, cp1, cp2-cp1);
					cp1 = cp2 + 1;	// in case no >
					cp2 = _tcschr(cp1, _T('>'));
					if (cp2)
						cp1 = cp2 + 1;
				}
				else
				{
					// no more tags, just copy rest
					_tcscat(buf2, cp1);
					break;
				}
			}

			TRACE(_T("GetPlainText:  buf1=<%s>\n"), buf1);
			TRACE(_T("GetPlainText:  buf2=<%s>\n"), buf2);

			if (plain)
			{
				_tcsncpy(plain, buf2, dwPlainSize-1);
				TRACE(_T("GetPlainText:  plain=<%s>\n"), plain);
				nSize = _tcslen(plain);
			}
			else
			{
				// no output buffer, just return required size
				nSize = _tcslen(buf2);
			}

			delete [] buf1;
			delete [] buf2;
		}
		else
		{
			// no html in string, just copy to output buffer
			if (plain)
			{
				_tcsncpy(plain, html, dwPlainSize-1);
				nSize = _tcslen(plain);
			}
			else
			{
				// no output buffer, just return required size
				nSize = _tcslen(html);
			}
		}
	}

	return (int)nSize;
}

///////////////////////////////////////////////////////////////////////////////
// IsOverAnchor
BOOL CXHtmlDraw::IsOverAnchor(HWND hWnd, XHTMLDRAWSTRUCT * pXHDS)
{
	TRACE(_T("in CXHtmlDraw::IsOverAnchor\n"));

	BOOL rc = FALSE;

	if (pXHDS && pXHDS->bHasAnchor && hWnd && ::IsWindow(hWnd))
	{
		POINT point;
		GetCursorPos(&point);
		::ScreenToClient(hWnd, &point);
		RECT rect = pXHDS->rectAnchor;
		//TRACERECT(rect);
		//TRACEPOINT(point);
		rc = ::PtInRect(&rect, point);
		if (rc)
		{
			TRACE(_T("mouse over anchor\n"));
		}
	}

	return rc;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Share

About the Author

Ahmed Charfeddine
Technical Lead
Tunisia Tunisia
Services:
http://www.pushframework.com/?page_id=890
Follow on   Twitter

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 14 Nov 2009
Article Copyright 2009 by Ahmed Charfeddine
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid