Click here to Skip to main content
15,884,960 members
Articles / Desktop Programming / MFC

CPPHtmlStatic v1.2

Rate me:
Please Sign up or sign in to vote.
4.94/5 (69 votes)
18 May 2012CPOL10 min read 305.5K   9.3K   185  
A control based on CStatic for displaying HTML-like text formatting elements.
#include "stdafx.h"
#include "PPHtmlDrawer.h"
#include "atlconv.h"    // for Unicode conversion - requires #include <afxdisp.h> // MFC OLE automation classes

#include <shellapi.h>
#pragma comment(lib, "comctl32.lib")



/*
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
*/

#define PPHTMLDRAWER_NO_HOVERLINK	-2	//A hot area is not exist under the cursor
#define PPHTMLDRAWER_BREAK_CHARS	_T(" -.,!:;)}]?") //A set of the chars to break line in the text wrap mode

enum {
		MODE_DRAW = 0,
		MODE_FIRSTPASS,
		MODE_SECONDPASS
	};

/*
#define m_szOffsetShadow.cx		4 //
#define m_szOffsetShadow.cy		4 //
#define m_szDepthShadow.cx		7 //
#define m_szDepthShadow.cy		7 //
#define PPHTMLDRAWER_SHADOW_COLOR		RGB (64, 64, 64) //A gradient shadow's color
*/


/////////////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer

CPPHtmlDrawer::CPPHtmlDrawer()
{
	m_nNumPass = MODE_FIRSTPASS;

	m_hInstDll = NULL;
	m_bFreeInstDll = FALSE;
	m_hDC = NULL;
	m_hImageList = NULL;
	
	m_csCallbackRepaint.hWnd = NULL;
	m_csCallbackRepaint.nMessage = 0;
	m_csCallbackRepaint.lParam = 0;
	m_csCallbackRepaint.wParam = 0;
	
	m_csCallbackLink.hWnd = NULL;
	m_csCallbackLink.nMessage = 0;
	m_csCallbackLink.lParam = 0;
	m_csCallbackLink.wParam = 0;

//	m_clrShadow = PPHTMLDRAWER_SHADOW_COLOR;

	m_hLinkCursor = NULL; // No cursor as yet
	m_nHoverIndexLink = PPHTMLDRAWER_NO_HOVERLINK;

	SetListOfTags();
	SetListSpecChars();
    SetTableOfColors();
	SetDefaultCursor();
	EnableEscapeSequences();
	SetMaxWidth(0);
//	EnableTextWrap(FALSE); //A text warpping was disabled by default
//	EnableTextWrap(TRUE); //A text warpping was disabled by default
	SetImageShadow(4, 4);
	SetTabSize(32);
	SetDefaultCssStyles();
	EnableOutput();
	SetDisabledColor(::GetSysColor(COLOR_BTNSHADOW));
}

CPPHtmlDrawer::~CPPHtmlDrawer()
{
	SetResourceDll(NULL);

	if (NULL != m_hLinkCursor)
	{
		::DestroyCursor(m_hLinkCursor);
		m_hLinkCursor = NULL;
	}
	
	if (NULL != m_hImageList)
		::DeleteObject(m_hImageList);
}

void CPPHtmlDrawer::EnableOutput(BOOL bEnable /* = TRUE */)
{
	m_bIsEnable = bEnable;
} //End of EnableOutput

void CPPHtmlDrawer::SetDisabledColor(COLORREF color)
{
	m_crDisabled = color;
}

HICON CPPHtmlDrawer::GetIconFromResources(DWORD dwID, int nWidth /* = 0 */, int nHeight /* = 0 */) const
{
	if (0 == dwID) return NULL;

	// Find correct resource handle
#ifdef _MFC_VER
	HINSTANCE hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(dwID), RT_GROUP_ICON);
#else
	HINSTANCE hInstResource = ::GetModuleHandle(NULL);
#endif
	// Set icon when the mouse is IN the button
	HICON hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(dwID), IMAGE_ICON, nWidth, nHeight, LR_DEFAULTCOLOR);
	
	return hIcon;
}

HICON CPPHtmlDrawer::GetIconFromFile(LPCTSTR lpszPath, int nWidth /* = 0 */, int nHeight /* = 0 */) const
{
	HICON hIcon = (HICON)::LoadImage(NULL, lpszPath, IMAGE_ICON, nWidth, nHeight, LR_LOADFROMFILE | LR_DEFAULTCOLOR);
	
	return hIcon;
}

HICON CPPHtmlDrawer::GetIconFromDll(DWORD dwID, int nWidth /* = 0 */, int nHeight /* = 0 */, LPCTSTR lpszPathDll /* = NULL */) const
{
	if (0 == dwID) return NULL;

	HICON hIcon = NULL;

	HINSTANCE hInstDll = NULL;
	BOOL bNewDll = FALSE;

	if (NULL == lpszPathDll)
	{
		if (NULL != m_hInstDll)
			hInstDll = m_hInstDll;
	}
	else
	{
		//Load New Library
		hInstDll = ::LoadLibraryEx(lpszPathDll, NULL, 0);
		if (NULL != hInstDll)
			bNewDll = TRUE;	
	}

	if (NULL != hInstDll)
	{
		hIcon = (HICON)::LoadImage(hInstDll, MAKEINTRESOURCE(dwID), IMAGE_ICON, nWidth, nHeight, LR_DEFAULTCOLOR);

		if (bNewDll)
			::FreeLibrary(hInstDll);
	}

	return hIcon;
}

HBITMAP CPPHtmlDrawer::GetBitmapFromResources(DWORD dwID) const
{
	if (0 == dwID) return NULL;

	// Find correct resource handle
#ifdef _MFC_VER
	HINSTANCE hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(dwID), RT_BITMAP);
#else
	HINSTANCE hInstResource = ::GetModuleHandle(NULL);
#endif
	// Load bitmap
	HBITMAP hBitmap = (HBITMAP)::LoadImage(hInstResource, MAKEINTRESOURCE(dwID), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
	
	return hBitmap;
}

HBITMAP CPPHtmlDrawer::GetBitmapFromFile(LPCTSTR lpszPath) const
{
	HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, lpszPath, IMAGE_BITMAP,
		0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);

	return hBitmap;
}

HBITMAP CPPHtmlDrawer::GetBitmapFromDll(DWORD dwID, LPCTSTR lpszPathDll /* = NULL */) const
{
	if (0 == dwID) return NULL;

	HBITMAP hBitmap = NULL;

	HINSTANCE hInstDll = NULL;
	BOOL bNewDll = FALSE;

	if (NULL == lpszPathDll)
	{
		if (NULL != m_hInstDll)
			hInstDll = m_hInstDll;
	}
	else
	{
		//Load New Library
		hInstDll = ::LoadLibraryEx(lpszPathDll, NULL, 0);
		if (NULL != hInstDll)
			bNewDll = TRUE;	
	}

	if (NULL != hInstDll)
	{
		hBitmap = (HBITMAP)::LoadImage(hInstDll, MAKEINTRESOURCE(dwID), IMAGE_BITMAP,
			0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);

		if (bNewDll)
			::FreeLibrary(hInstDll);
	}

	return hBitmap;
}

CPPString CPPHtmlDrawer::GetStringFromResource(DWORD dwID) const
{
	if (0 == dwID) return _T("");

	CPPString str;
	str.LoadString(dwID);

	return str;
}

CPPString CPPHtmlDrawer::GetStringFromDll(DWORD dwID, LPCTSTR lpszPathDll /* = NULL */) const
{
	if (0 == dwID) return _T("");

	CPPString str = _T("");

	HINSTANCE hInstDll = NULL;
	BOOL bNewDll = FALSE;

	if (NULL == lpszPathDll)
	{
		if (NULL != m_hInstDll)
			hInstDll = m_hInstDll;
	}
	else
	{
		//Load New Library
		hInstDll = ::LoadLibraryEx(lpszPathDll, NULL, 0);
		if (NULL != hInstDll)
			bNewDll = TRUE;	
	}

	if (NULL != hInstDll)
	{
#ifdef _UNICODE
#define CHAR_FUDGE 1    // one TCHAR unused is good enough
#else
#define CHAR_FUDGE 2    // two BYTES unused for case of DBC last char
#endif
		// try fixed buffer first (to avoid wasting space in the heap)
		TCHAR szTemp[256];
		
		DWORD dwLen = ::LoadString(hInstDll, dwID, szTemp, (sizeof(szTemp) * sizeof(TCHAR)));
		// If resource not found (or ::LoadString failure)
		if (0 != dwLen) 
		{
			if ((sizeof(szTemp) * sizeof(TCHAR)) - dwLen > CHAR_FUDGE)
			{
				str = szTemp;
			} // if
			else
			{
				// try buffer size of 512, then larger size until entire string is retrieved
				int nSize = 256;
				do
				{
					nSize += 256;
					dwLen = ::LoadString(hInstDll, dwID, str.GetBuffer(nSize-1), nSize);
				} while (nSize - dwLen <= CHAR_FUDGE);
				str.ReleaseBuffer();
			}
#undef CHAR_FUDGE
		}

		if (bNewDll)
			::FreeLibrary(hInstDll);
	}
	return str;
}

///////////////////////////////////////////////////////////
// Get tooltip string for menu and toolbar items from the 
// resources of the application.
// 
// Parameters:
//		nID - Resource ID of the string
//		nNumParam - Which parameter will gets:
//					 0=long,
//					 1=short,
//					 2=disable
//
//
// Format prompt string:  long prompt \n short prompt \n disable prompt
////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::GetResCommandPrompt(UINT nID, UINT nNumParam /* = 0 */)
{
	CPPString str = GetStringFromResource(nID);
	if (!str.IsEmpty())
	{
		int nFirst = 0;
		int nLast = nFirst;
		UINT nCount = 0;
		while ((nCount <= nNumParam) && (nFirst < str.GetLength()))
		{
			nLast = str.Find(_T('\n'), nFirst);
			if (nLast < 0)
			{
				//Char wasn't found
				if (nCount == nNumParam)
					str = str.Mid(nFirst, str.GetLength() - nFirst);
				else
					str.Empty();
				
				return str;
			}
			else
			{
				//Char was found
				if (nCount == nNumParam)
				{
					str = str.Mid(nFirst, nLast - nFirst);
					return str;
				}
				else
				{
					nFirst = nLast + 1;
				} //if
			} //if
			nCount ++;
		} //while
	} //if

	return _T("");
} //End of GetResCommandPrompt

/////////////////////////////////////////////////////////////////////////////
// 
void CPPHtmlDrawer::SetListSpecChars()
{
	AddSpecChar(_T("&amp;"), _T("&"));			// ampersand
	AddSpecChar(_T("&bull;"), _T("\x95\0"));	// bullet  NOT IN MS SANS SERIF
	AddSpecChar(_T("&copy;"), _T("\xA9\0"));	// copyright
//	AddSpecChar(_T("&euro;"), _T("\x80\0"));	// euro sign IN NOT CYRILLIC FONTS
	AddSpecChar(_T("&euro;"), _T("\x88\0"));	// euro sign IN CYRILLIC FONTS
	AddSpecChar(_T("&gt;"), _T(">"));			// greater than
	AddSpecChar(_T("&iquest;"), _T("\xBF\0"));	// inverted question mark
	AddSpecChar(_T("&lt;"), _T("<<"));			// less than
	AddSpecChar(_T("&nbsp;"), _T(" "));			// nonbreaking space
	AddSpecChar(_T("&para;"), _T("\xB6\0"));	// paragraph sign
	AddSpecChar(_T("&pound;"), _T("\xA3\0"));	// pound sign
	AddSpecChar(_T("&quot;"), _T("\""));		// quotation mark
	AddSpecChar(_T("&reg;"), _T("\xAE\0"));		// registered trademark
	AddSpecChar(_T("&trade;"), _T("\x99\0"));	// trademark NOT IN MS SANS SERIF
} //End of SetListSpecChars

void CPPHtmlDrawer::AddSpecChar(LPCTSTR lpszAlias, LPCTSTR lpszValue)
{
	iter_mapStyles iter = m_mapSpecChars.find(lpszAlias);
	
	if (iter != m_mapSpecChars.end())
		iter->second = lpszValue;		//Modifies
	else
		m_mapSpecChars.insert(std::make_pair(lpszAlias, lpszValue)); //Add new
} //End of AddSpecialChar

void CPPHtmlDrawer::ReplaceSpecChars()
{
	CPPString sAlias, sValue;
	for (iter_mapStyles iter = m_mapSpecChars.begin(); iter != m_mapSpecChars.end(); ++iter)
	{
		sAlias = iter->first;
		sValue = iter->second;
		m_csHtmlText.Replace(sAlias, sValue);
	} //for

	m_csHtmlText.Remove(_T('\r'));
	if (!m_bEnableEscapeSequences)
	{
		//ENG: Remove escape sequences
		//RUS: ������� ����������� �������
		m_csHtmlText.Remove(_T('\n'));
		m_csHtmlText.Remove(_T('\t'));
	}
	else
	{
		//ENG: Replace escape sequences to HTML tags
		//RUS: �������� ����������� ������� HTML ������
		m_csHtmlText.Replace(_T("\n"), _T("<br>"));
		m_csHtmlText.Replace(_T("\t"), _T("<t>"));
	} //if
} //End of ReplaceSpecChars

/////////////////////////////////////////////////////////////////////////////
// 
void CPPHtmlDrawer::SetListOfTags()
{
	AddTagToList(_T("b"), TAG_BOLD, _T("bold"));
	AddTagToList(_T("i"), TAG_ITALIC, _T("italic"));
	AddTagToList(_T("em"), TAG_ITALIC, _T("italic"));
	AddTagToList(_T("u"), TAG_UNDERLINE, _T("underline"));
	AddTagToList(_T("s"), TAG_STRIKEOUT, _T("strikeout"));
	AddTagToList(_T("strike"), TAG_STRIKEOUT, _T("strikeout"));
	AddTagToList(_T("font"), TAG_FONT, _T("font"));
	AddTagToList(_T("hr"), TAG_HLINE, _T(""));
	AddTagToList(_T("br"), TAG_NEWLINE, _T(""));
	AddTagToList(_T("\n"), TAG_NEWLINE, _T(""));
	AddTagToList(_T("t"), TAG_TABULATION, _T(""));
	AddTagToList(_T("\t"), TAG_TABULATION, _T(""));
	AddTagToList(_T("left"), TAG_LEFT, _T("left"));
	AddTagToList(_T("center"), TAG_CENTER, _T("center"));
	AddTagToList(_T("right"), TAG_RIGHT, _T("right"));
	AddTagToList(_T("justify"), TAG_JUSTIFY, _T("justify"));
	AddTagToList(_T("baseline"), TAG_BASELINE, _T("baseline"));
	AddTagToList(_T("top"), TAG_TOP, _T("top"));
	AddTagToList(_T("vcenter"), TAG_VCENTER, _T("vcenter"));
	AddTagToList(_T("middle"), TAG_VCENTER, _T("vcenter"));
	AddTagToList(_T("bottom"), TAG_BOTTOM, _T("vcenter"));
	AddTagToList(_T("bmp"), TAG_BITMAP, _T(""));
	AddTagToList(_T("icon"), TAG_ICON, _T(""));
	AddTagToList(_T("ilst"), TAG_IMAGELIST, _T(""));
	AddTagToList(_T("string"), TAG_STRING, _T(""));
	AddTagToList(_T("body"), TAG_NEWSTYLE, _T("body"));
	AddTagToList(_T("h1"), TAG_NEWSTYLE, _T("h1"));
	AddTagToList(_T("h2"), TAG_NEWSTYLE, _T("h2"));
	AddTagToList(_T("h3"), TAG_NEWSTYLE, _T("h3"));
	AddTagToList(_T("h4"), TAG_NEWSTYLE, _T("h4"));
	AddTagToList(_T("h5"), TAG_NEWSTYLE, _T("h5"));
	AddTagToList(_T("h6"), TAG_NEWSTYLE, _T("h6"));
	AddTagToList(_T("code"), TAG_NEWSTYLE, _T("code"));
	AddTagToList(_T("pre"), TAG_NEWSTYLE, _T("pre"));
	AddTagToList(_T("big"), TAG_NEWSTYLE, _T("big"));
	AddTagToList(_T("small"), TAG_NEWSTYLE, _T("small"));
	AddTagToList(_T("sub"), TAG_NEWSTYLE, _T("sub"));
	AddTagToList(_T("sup"), TAG_NEWSTYLE, _T("sup"));
	AddTagToList(_T("span"), TAG_SPAN, _T("span"));
	AddTagToList(_T("a"), TAG_HYPERLINK, _T("link"));
} //End of SetListOfTags

////////////////////////////////////////////////////////////////////////
// Format for the new tags:
//		lpszName		- a tag's name in the HTML string
//		dwTagIndex		- ID of the tag
//		lpszFullName	- a custom name if tag must be closing. Empty if not.  
////////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::AddTagToList(LPCTSTR lpszName, DWORD dwTagIndex, LPCTSTR lpszFullName)
{
	STRUCT_TAGPROP tp;
	tp.dwTagIndex = dwTagIndex;
	tp.strTagName = lpszFullName;

	iterMapTags iterMap = m_mapTags.find(lpszName);
	
	if (iterMap != m_mapTags.end())
		iterMap->second = tp; //Modifies
	else
		m_mapTags.insert(std::make_pair(lpszName, tp)); //Add new
} //End of AddTagToList

DWORD CPPHtmlDrawer::GetTagFromList(CPPString sTagName, CPPString & strFullName, BOOL & bCloseTag)
{
	strFullName.Empty();

	bCloseTag = (sTagName.GetAt(0) == _T('/')) ? TRUE : FALSE;
	if (bCloseTag)
		sTagName = sTagName.Mid(1);

	iterMapTags iterMap = m_mapTags.find(sTagName);
	
	if (iterMap != m_mapTags.end())
	{
		STRUCT_TAGPROP tp = iterMap->second;
		strFullName = tp.strTagName;
		
		return tp.dwTagIndex;
	} //if

	return TAG_NONE;
} //End of GetTagFromList

///////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////
void CPPHtmlDrawer::SetTableOfColors()
{
	//Frequency used
	SetColorName(_T("aqua"), RGB(0x00, 0xFF, 0xFF)); 
	SetColorName(_T("black"), RGB(0x00, 0x00, 0x00)); 
	SetColorName(_T("blue"), RGB(0x00, 0x00, 0xFF)); 
	SetColorName(_T("brown"), RGB(0xA5, 0x2A, 0x2A)); 
	SetColorName(_T("cyan"), RGB(0x00, 0xFF, 0xFF));
	SetColorName(_T("gold"), RGB(0xFF, 0xD7, 0x00)); 
	SetColorName(_T("gray"), RGB(0x80, 0x80, 0x80)); 
	SetColorName(_T("green"), RGB(0x00, 0x80, 0x00)); 
	SetColorName(_T("magenta"), RGB(0xFF, 0x00, 0xFF)); 
	SetColorName(_T("maroon"), RGB(0x80, 0x00, 0x00)); 
	SetColorName(_T("navy"), RGB(0x00, 0x00, 0x80)); 
	SetColorName(_T("olive"), RGB(0x80, 0x80, 0x00)); 
	SetColorName(_T("orange"), RGB(0xFF, 0xA5, 0x00)); 
	SetColorName(_T("pink"), RGB(0xFF, 0xC0, 0xCB)); 
	SetColorName(_T("purple"), RGB(0x80, 0x00, 0x80)); 
	SetColorName(_T("red"), RGB(0xFF, 0x00, 0x00)); 
	SetColorName(_T("silver"), RGB(0xC0, 0xC0, 0xC0)); 
	SetColorName(_T("snow"), RGB(0xFF, 0xFA, 0xFA)); 
	SetColorName(_T("violet"), RGB(0xEE, 0x82, 0xEE)); 
	SetColorName(_T("white"), RGB(0xFF, 0xFF, 0xFF)); 
	SetColorName(_T("yellow"), RGB(0xFF, 0xFF, 0x00)); 

	//Common Used
	SetColorName(_T("aliceblue"), RGB(0xF0, 0xF8, 0xFF)); 
	SetColorName(_T("antiquewhite"), RGB(0xFA, 0xEB, 0xD7)); 
	SetColorName(_T("aquamarine"), RGB(0x7F, 0xFF, 0xD4)); 
	SetColorName(_T("azure"), RGB(0xF0, 0xFF, 0xFF)); 
	SetColorName(_T("beige"), RGB(0xF5, 0xF5, 0xDC)); 
	SetColorName(_T("bisque"), RGB(0xFF, 0xE4, 0xC4));
	SetColorName(_T("blanchedalmond"), RGB(0xFF, 0xEB, 0xCD)); 
	SetColorName(_T("blueviolet"), RGB(0x8A, 0x2B, 0xE2)); 
	SetColorName(_T("burlywood"), RGB(0xDE, 0xB8, 0x87)); 
	SetColorName(_T("cadetblue"), RGB(0x5F, 0x9E, 0xA0)); 
	SetColorName(_T("chartreuse"), RGB(0x7F, 0xFF, 0x00)); 
	SetColorName(_T("chocolate"), RGB(0xD2, 0x69, 0x1E)); 
	SetColorName(_T("coral"), RGB(0xFF, 0x7F, 0x50)); 
	SetColorName(_T("cornflowerblue"), RGB(0x64, 0x95, 0xED)); 
	SetColorName(_T("cornsilk"), RGB(0xFF, 0xF8, 0xDC)); 
	SetColorName(_T("crimson"), RGB(0xDC, 0x14, 0x3C)); 
	SetColorName(_T("darkblue"), RGB(0x00, 0x00, 0x8B)); 
	SetColorName(_T("darkcyan"), RGB(0x00, 0x8B, 0x8B)); 
	SetColorName(_T("darkgoldenrod"), RGB(0xB8, 0x86, 0x0B)); 
	SetColorName(_T("darkgray"), RGB(0xA9, 0xA9, 0xA9)); 
	SetColorName(_T("darkgreen"), RGB(0x00, 0x64, 0x00)); 
	SetColorName(_T("darkkhaki"), RGB(0xBD, 0xB7, 0x6B)); 
	SetColorName(_T("darkmagenta"), RGB(0x8B, 0x00, 0x8B)); 
	SetColorName(_T("darkolivegreen"), RGB(0x55, 0x6B, 0x2F)); 
	SetColorName(_T("darkorange"), RGB(0xFF, 0x8C, 0x00)); 
	SetColorName(_T("darkorchid"), RGB(0x99, 0x32, 0xCC)); 
	SetColorName(_T("darkred"), RGB(0x8B, 0x00, 0x00)); 
	SetColorName(_T("darksalmon"), RGB(0xE9, 0x96, 0x7A)); 
	SetColorName(_T("darkseagreen"), RGB(0x8F, 0xBC, 0x8B)); 
	SetColorName(_T("darkslateblue"), RGB(0x48, 0x3D, 0x8B)); 
	SetColorName(_T("darkslategray"), RGB(0x2F, 0x4F, 0x4F)); 
	SetColorName(_T("darkturquoise"), RGB(0x00, 0xCE, 0xD1)); 
	SetColorName(_T("darkviolet"), RGB(0x94, 0x00, 0xD3)); 
	SetColorName(_T("deeppink"), RGB(0xFF, 0x14, 0x93)); 
	SetColorName(_T("deepskyblue"), RGB(0x00, 0xBF, 0xFF)); 
	SetColorName(_T("dimgray"), RGB(0x69, 0x69, 0x69)); 
	SetColorName(_T("dodgerblue"), RGB(0x1E, 0x90, 0xFF)); 
	SetColorName(_T("firebrick"), RGB(0xB2, 0x22, 0x22)); 
	SetColorName(_T("floralwhite"), RGB(0xFF, 0xFA, 0xF0)); 
	SetColorName(_T("forestgreen"), RGB(0x22, 0x8B, 0x22)); 
	SetColorName(_T("fuchsia"), RGB(0xFF, 0x00, 0xFF)); 
	SetColorName(_T("gainsboro"), RGB(0xDC, 0xDC, 0xDC)); 
	SetColorName(_T("ghostwhite"), RGB(0xF8, 0xF8, 0xFF)); 
	SetColorName(_T("goldenrod"), RGB(0xDA, 0xA5, 0x20)); 
	SetColorName(_T("greenyellow"), RGB(0xAD, 0xFF, 0x2F)); 
	SetColorName(_T("honeydew"), RGB(0xF0, 0xFF, 0xF0)); 
	SetColorName(_T("hotpink"), RGB(0xFF, 0x69, 0xB4)); 
	SetColorName(_T("indianred"), RGB(0xCD, 0x5C, 0x5C)); 
	SetColorName(_T("indigo"), RGB(0x4B, 0x00, 0x82)); 
	SetColorName(_T("ivory"), RGB(0xFF, 0xFF, 0xF0)); 
	SetColorName(_T("khaki"), RGB(0xF0, 0xE6, 0x8C)); 
	SetColorName(_T("lavender"), RGB(0xE6, 0xE6, 0xFA)); 
	SetColorName(_T("lavenderblush"), RGB(0xFF, 0xF0, 0xF5)); 
	SetColorName(_T("lawngreen"), RGB(0x7C, 0xFC, 0x00)); 
	SetColorName(_T("lemonchiffon"), RGB(0xFF, 0xFA, 0xCD)); 
	SetColorName(_T("lightblue"), RGB(0xAD, 0xD8, 0xE6)); 
	SetColorName(_T("lightcoral"), RGB(0xF0, 0x80, 0x80)); 
	SetColorName(_T("lightcyan"), RGB(0xE0, 0xFF, 0xFF));
	SetColorName(_T("lightgoldenrodyellow"), RGB(0xFA, 0xFA, 0xD2)); 
	SetColorName(_T("lightgreen"), RGB(0x90, 0xEE, 0x90)); 
	SetColorName(_T("lightgrey"), RGB(0xD3, 0xD3, 0xD3)); 
	SetColorName(_T("lightpink"), RGB(0xFF, 0xB6, 0xC1)); 
	SetColorName(_T("lightsalmon"), RGB(0xFF, 0xA0, 0x7A)); 
	SetColorName(_T("lightseagreen"), RGB(0x20, 0xB2, 0xAA)); 
	SetColorName(_T("lightskyblue"), RGB(0x87, 0xCE, 0xFA)); 
	SetColorName(_T("lightslategray"), RGB(0x77, 0x88, 0x99)); 
	SetColorName(_T("lightsteelblue"), RGB(0xB0, 0xC4, 0xDE));
	SetColorName(_T("lightyellow"), RGB(0xFF, 0xFF, 0xE0)); 
	SetColorName(_T("lime"), RGB(0x00, 0xFF, 0x00)); 
	SetColorName(_T("limegreen"), RGB(0x32, 0xCD, 0x32)); 
	SetColorName(_T("linen"), RGB(0xFA, 0xF0, 0xE6)); 
	SetColorName(_T("mediumaquamarine"), RGB(0x66, 0xCD, 0xAA)); 
	SetColorName(_T("mediumblue"), RGB(0x00, 0x00, 0xCD)); 
	SetColorName(_T("mediumorchid"), RGB(0xBA, 0x55, 0xD3)); 
	SetColorName(_T("mediumpurple"), RGB(0x93, 0x70, 0xDB)); 
	SetColorName(_T("mediumseagreen"), RGB(0x3C, 0xB3, 0x71)); 
	SetColorName(_T("mediumslateblue"), RGB(0x7B, 0x68, 0xEE)); 
	SetColorName(_T("mediumspringgreen"), RGB(0x00, 0xFA, 0x9A)); 
	SetColorName(_T("mediumturquoise"), RGB(0x48, 0xD1, 0xCC)); 
	SetColorName(_T("mediumvioletred"), RGB(0xC7, 0x15, 0x85)); 
	SetColorName(_T("midnightblue"), RGB(0x19, 0x19, 0x70)); 
	SetColorName(_T("mintcream"), RGB(0xF5, 0xFF, 0xFA)); 
	SetColorName(_T("mistyrose"), RGB(0xFF, 0xE4, 0xE1)); 
	SetColorName(_T("moccasin"), RGB(0xFF, 0xE4, 0xB5)); 
	SetColorName(_T("navajowhite"), RGB(0xFF, 0xDE, 0xAD)); 
	SetColorName(_T("oldlace"), RGB(0xFD, 0xF5, 0xE6)); 
	SetColorName(_T("olivedrab"), RGB(0x6B, 0x8E, 0x23)); 
	SetColorName(_T("orangered"), RGB(0xFF, 0x45, 0x00)); 
	SetColorName(_T("orchid"), RGB(0xDA, 0x70, 0xD6)); 
	SetColorName(_T("palegoldenrod"), RGB(0xEE, 0xE8, 0xAA)); 
	SetColorName(_T("palegreen"), RGB(0x98, 0xFB, 0x98)); 
	SetColorName(_T("paleturquoise"), RGB(0xAF, 0xEE, 0xEE)); 
	SetColorName(_T("palevioletred"), RGB(0xDB, 0x70, 0x93)); 
	SetColorName(_T("papayawhip"), RGB(0xFF, 0xEF, 0xD5));
	SetColorName(_T("peachpuff"), RGB(0xFF, 0xDA, 0xB9)); 
	SetColorName(_T("peru"), RGB(0xCD, 0x85, 0x3F)); 
	SetColorName(_T("plum"), RGB(0xDD, 0xA0, 0xDD)); 
	SetColorName(_T("powderblue"), RGB(0xB0, 0xE0, 0xE6)); 
	SetColorName(_T("rosybrown"), RGB(0xBC, 0x8F, 0x8F)); 
	SetColorName(_T("royalblue"), RGB(0x41, 0x69, 0xE1)); 
	SetColorName(_T("saddlebrown"), RGB(0x8B, 0x45, 0x13)); 
	SetColorName(_T("salmon"), RGB(0xFA, 0x80, 0x72)); 
	SetColorName(_T("sandybrown"), RGB(0xF4, 0xA4, 0x60)); 
	SetColorName(_T("seagreen"), RGB(0x2E, 0x8B, 0x57)); 
	SetColorName(_T("seashell"), RGB(0xFF, 0xF5, 0xEE)); 
	SetColorName(_T("sienna"), RGB(0xA0, 0x52, 0x2D)); 
	SetColorName(_T("skyblue"), RGB(0x87, 0xCE, 0xEB)); 
	SetColorName(_T("slateblue"), RGB(0x6A, 0x5A, 0xCD)); 
	SetColorName(_T("slategray"), RGB(0x70, 0x80, 0x90)); 
	SetColorName(_T("springgreen"), RGB(0x00, 0xFF, 0x7F)); 
	SetColorName(_T("steelblue"), RGB(0x46, 0x82, 0xB4)); 
	SetColorName(_T("tan"), RGB(0xD2, 0xB4, 0x8C)); 
	SetColorName(_T("teal"), RGB(0x00, 0x80, 0x80)); 
	SetColorName(_T("thistle"), RGB(0xD8, 0xBF, 0xD8)); 
	SetColorName(_T("tomato"), RGB(0xFF, 0x63, 0x47)); 
	SetColorName(_T("turquoise"), RGB(0x40, 0xE0, 0xD0)); 
	SetColorName(_T("wheat"), RGB(0xF5, 0xDE, 0xB3)); 
	SetColorName(_T("whitesmoke"), RGB(0xF5, 0xF5, 0xF5)); 
	SetColorName(_T("yellowgreen"), RGB(0x9A, 0xCD, 0x32));

	//Systems colors
	SetColorName(_T("activeborder"), ::GetSysColor(COLOR_ACTIVEBORDER)); 
	SetColorName(_T("activecaption"), ::GetSysColor(COLOR_ACTIVECAPTION)); 
	SetColorName(_T("appworkspace"), ::GetSysColor(COLOR_APPWORKSPACE)); 
	SetColorName(_T("background"), ::GetSysColor(COLOR_BACKGROUND)); 
	SetColorName(_T("buttonface"), ::GetSysColor(COLOR_BTNFACE)); 
	SetColorName(_T("buttonhighlight"), ::GetSysColor(COLOR_BTNHILIGHT)); 
	SetColorName(_T("buttonshadow"), ::GetSysColor(COLOR_BTNSHADOW)); 
	SetColorName(_T("buttontext"), ::GetSysColor(COLOR_BTNTEXT)); 
	SetColorName(_T("captiontext"), ::GetSysColor(COLOR_CAPTIONTEXT)); 
	SetColorName(_T("graytext"), ::GetSysColor(COLOR_GRAYTEXT)); 
	SetColorName(_T("highlight"), ::GetSysColor(COLOR_HIGHLIGHT)); 
	SetColorName(_T("highlighttext"), ::GetSysColor(COLOR_HIGHLIGHTTEXT)); 
	SetColorName(_T("inactiveborder"), ::GetSysColor(COLOR_INACTIVEBORDER)); 
	SetColorName(_T("inactivecaption"), ::GetSysColor(COLOR_INACTIVECAPTION)); 
	SetColorName(_T("inactivecaptiontext"), ::GetSysColor(COLOR_INACTIVECAPTIONTEXT)); 
	SetColorName(_T("infobackground"), ::GetSysColor(COLOR_INFOBK)); 
	SetColorName(_T("infotext"), ::GetSysColor(COLOR_INFOTEXT)); 
	SetColorName(_T("menu"), ::GetSysColor(COLOR_MENU)); 
	SetColorName(_T("menutext"), ::GetSysColor(COLOR_MENUTEXT)); 
	SetColorName(_T("scrollbar"), ::GetSysColor(COLOR_SCROLLBAR)); 
	SetColorName(_T("threeddarkshadow"), ::GetSysColor(COLOR_3DDKSHADOW)); 
	SetColorName(_T("threedface"), ::GetSysColor(COLOR_3DFACE)); 
	SetColorName(_T("threedhighlight"), ::GetSysColor(COLOR_3DHIGHLIGHT)); 
	SetColorName(_T("threedlightshadow"), ::GetSysColor(COLOR_3DLIGHT)); 
	SetColorName(_T("threedshadow"), ::GetSysColor(COLOR_3DSHADOW)); 
	SetColorName(_T("window"), ::GetSysColor(COLOR_WINDOW)); 
	SetColorName(_T("windowframe"), ::GetSysColor(COLOR_WINDOWFRAME)); 
	SetColorName(_T("windowtext"), ::GetSysColor(COLOR_WINDOWTEXT)); 
} //End SetTableOfColors

void CPPHtmlDrawer::SetColorName(LPCTSTR lpszColorName, COLORREF color)
{
	iterMapColors iterMap = m_mapColors.find(lpszColorName);
	
	if (iterMap != m_mapColors.end())
		iterMap->second = color; //Modifies
	else
		m_mapColors.insert(std::make_pair(lpszColorName, color)); //Add new
} //End SetColorName

COLORREF CPPHtmlDrawer::GetColorByName(LPCTSTR lpszColorName, COLORREF crDefColor /* = RGB(0, 0, 0) */)
{
	if (m_bIsEnable)
	{
		iterMapColors iterMap = m_mapColors.find(lpszColorName);
		
		if (iterMap != m_mapColors.end())
			crDefColor = iterMap->second;
	}
	else
	{
		//For disabled output
		crDefColor = m_crDisabled;
	} //if
	return crDefColor;
} //End GetColorByName

/////////////////////////////////////////////////////////////////
// Gets the system tooltip's logfont
/////////////////////////////////////////////////////////////////
LPLOGFONT CPPHtmlDrawer::GetSystemToolTipFont() const
{
    static LOGFONT lf;
	
    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(NONCLIENTMETRICS);
    if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0))
        return FALSE;
	
    memcpy(&lf, &(ncm.lfStatusFont), sizeof(LOGFONT));
	
    return &lf; 
} //End GetSystemToolTipFont

////////////////////////////////////////////
// Check a pointer over the hyperlink
//   In: lpPoint - the coordinates of the mouse pointer 
//  Out: -1 - hyperlink not found
//       index of the hyperlink
////////////////////////////////////////////
int CPPHtmlDrawer::PtInHyperlink(LPPOINT lpPoint)
{
	for (UINT i = 0; i < m_arrLinks.size(); ++i)
	{
		STRUCT_HYPERLINK & link = m_arrLinks [i];
		if ((link.rcArea.left <= lpPoint->x) && (link.rcArea.right >= lpPoint->x) &&
			(link.rcArea.top <= lpPoint->y) && (link.rcArea.bottom >= lpPoint->y))
			return i;
	} //for
	return -1;
} //End PtInHyperlink

void CPPHtmlDrawer::JumpToHyperlink(int nLink)
{
	STRUCT_HYPERLINK & link = m_arrLinks [nLink];
//	TRACE(_T("Jump to Hyperlink number = %d\n"), nLink);
	if (!link.sHyperlink.IsEmpty())
	{
		switch (link.nTypeLink)
		{
		case LINK_HREF:
			GotoURL(link.sHyperlink);
			break;
		case LINK_MESSAGE:
			CallbackOnClickHyperlink(link.sHyperlink);
			break;
		} //switch
	} //if
} //End JumpToHyperlink

void CPPHtmlDrawer::OnLButtonDown(LPPOINT lpClient)
{
//	TRACE (_T("CPPHtmlDrawer::OnLButtonDown()\n"));
	
	int nLink = PtInHyperlink(lpClient);
	if (nLink >= 0)
	{
		//Hyperlink under the mouse pointer
		JumpToHyperlink(nLink);
	} //if
} //End OnLButtonDown

BOOL CPPHtmlDrawer::OnSetCursor(LPPOINT lpClient)
{
	int nLink = PtInHyperlink(lpClient);
	if (nLink >= 0)
	{
		STRUCT_HYPERLINK link = m_arrLinks [nLink];
		if (m_nHoverIndexLink != link.nIndexLink)
		{
			m_nHoverIndexLink = link.nIndexLink;
			CallbackOnRepaint(m_nHoverIndexLink);
			//Redraw Window
		} //if
		
		if (!link.sHyperlink.IsEmpty() && (NULL != m_hLinkCursor))
		{
			::SetCursor(m_hLinkCursor);
			return TRUE;
		} //if
	}
	else if (m_nHoverIndexLink != PPHTMLDRAWER_NO_HOVERLINK)
	{
		m_nHoverIndexLink = PPHTMLDRAWER_NO_HOVERLINK;
		CallbackOnRepaint(m_nHoverIndexLink);
		//Redraw Window
	} //if
	
    return FALSE;
} //End OnSetCursor

BOOL CPPHtmlDrawer::OnTimer()
{
	BOOL bRedraw = FALSE;
	if (m_arrAni.size() > 0)
	{
		for (UINT i = 0; i < m_arrAni.size(); ++i)
		{
			STRUCT_ANIMATION & sa = m_arrAni [i];
			if (sa.nMaxImages > 0)
			{
				sa.nTimerCount ++;
				if (sa.nTimerCount >= sa.nSpeed)
				{
					sa.nTimerCount = 0;
					sa.nIndex ++;
					if (sa.nIndex >= sa.nMaxImages)
						sa.nIndex = 0;
					bRedraw = TRUE;
				} //if
				m_arrAni [i] = sa;
			} //if
		} //for
	} //if

	return bRedraw;
} //End of OnTimer

void CPPHtmlDrawer::CallbackOnRepaint(int nIndexLink)
{
//	TRACE(_T("CPPHtmlDrawer::CallbackOnRepaint()\n")); 

	if ((NULL == m_csCallbackRepaint.hWnd) || !m_csCallbackRepaint.nMessage)
		return; 
 	
	::SendMessage(m_csCallbackRepaint.hWnd, m_csCallbackRepaint.nMessage, (LPARAM)nIndexLink, m_csCallbackRepaint.lParam);  
} //End CallbackOnRepaint

void CPPHtmlDrawer::CallbackOnClickHyperlink(LPCTSTR sLink)
{
//	TRACE(_T("CPPHtmlDrawer::CallbackOnClickHyperlink()\n")); 

	if ((NULL == m_csCallbackLink.hWnd) || !m_csCallbackLink.nMessage)
		return; 
	
	::SendMessage(m_csCallbackLink.hWnd, m_csCallbackLink.nMessage, (LPARAM)sLink, m_csCallbackLink.lParam);  	
} //if CallbackOnClickHyperlink

HINSTANCE CPPHtmlDrawer::GotoURL(LPCTSTR url, int showcmd /* = SW_SHOW */)
{
	SetHyperlinkCursor(NULL);

    TCHAR key[MAX_PATH + MAX_PATH];

    // First try ShellExecute()
    HINSTANCE result = ShellExecute(NULL, _T("open"), url, NULL, NULL, showcmd);

    // If it failed, get the .htm regkey and lookup the program
    if ((UINT)result <= HINSTANCE_ERROR) 
	{

        if (GetRegKey(HKEY_CLASSES_ROOT, _T(".htm"), key) == ERROR_SUCCESS) 
		{
            lstrcat(key, _T("\\shell\\open\\command"));

            if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) 
			{
                TCHAR *pos;
                pos = _tcsstr(key, _T("\"%1\""));
                if (pos == NULL) 
				{                     // No quotes found
                    pos = _tcsstr(key, _T("%1"));      // Check for %1, without quotes 
                    if (pos == NULL)                   // No parameter at all...
                        pos = key+lstrlen(key)-1;
                    else
                        *pos = '\0';                   // Remove the parameter
                }
                else
                    *pos = '\0';                       // Remove the parameter

                lstrcat(pos, _T(" "));
                lstrcat(pos, url);

                USES_CONVERSION;
                result = (HINSTANCE) WinExec(T2A(key),showcmd);
            } //if
        } //if
    } //if
    return result;
} //End GotoURL

LONG CPPHtmlDrawer::GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
{
    HKEY hkey;
    LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);

    if (retval == ERROR_SUCCESS) {
        long datasize = MAX_PATH;
        TCHAR data[MAX_PATH];
        RegQueryValue(hkey, NULL, data, &datasize);
        lstrcpy(retdata,data);
        RegCloseKey(hkey);
    } //if

    return retval;
} //End GetRegKey

/////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::DrawHtml (LPSIZE lpSize, LPCRECT lpRect)
{
	//ENG: Bounding rectangle of a current area for output
	//RUS: �������������� ������������� ��� ������� ������� ������
	RECT rcArea;
	rcArea.left = lpRect->left;
	rcArea.right = lpRect->right;
	rcArea.top = lpRect->top;
	rcArea.bottom = lpRect->bottom;

	SIZE szArea;
	szArea.cx = szArea.cy = 0;
	
	if (MODE_FIRSTPASS == m_nNumPass)
	{
		//ENG: In preparing mode clears an auxiliary tables
		//RUS: � ������ ���������� ������� ��������������� ������� 
		m_arrLinks.clear();
//		m_arrTable.clear();
		m_arrHtmlLine.clear();
//		m_arrTableSizes.clear();
		m_arrAni.clear();
	} //if

	m_nCurLine = 0;
	m_nCurTable = -1;
	m_nNumCurTable = -1;
	m_nCurIndexLink = -1;
	m_nCurIndexAni = -1;
	
	//ENG: Clear stack of tags
	//RUS: ������� ���� �����
	m_arrStack.clear();
	
	int nIndex = 0;
	int nBegin;
	CPPString strText;
	
	//ENG: Applies a default styles
	//RUS: ��������� ����� ��-���������
	SetDefaultStyles(m_defStyle);
	SelectNewHtmlStyle(_T("body"), m_defStyle);
	
	//ENG: Creates a default font
	//RUS: ������� ����� �� ���������
	m_lfDefault.lfHeight = m_defStyle.nSizeFont;
	m_lfDefault.lfWidth = 0;
	m_lfDefault.lfOrientation = 0;
	m_lfDefault.lfEscapement = 0;
	m_lfDefault.lfWeight = m_defStyle.nWeightFont;
	m_lfDefault.lfItalic = m_defStyle.bItalicFont;
	m_lfDefault.lfStrikeOut = m_defStyle.bStrikeOutFont;
	m_lfDefault.lfUnderline = m_defStyle.bUnderlineFont;
	m_lfDefault.lfCharSet = DEFAULT_CHARSET;
	m_lfDefault.lfOutPrecision = OUT_DEFAULT_PRECIS;
	m_lfDefault.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	m_lfDefault.lfQuality = DEFAULT_QUALITY;
	m_lfDefault.lfPitchAndFamily = FF_DONTCARE;
	_tcscpy (m_lfDefault.lfFaceName, m_defStyle.sFaceFont);
	m_hFont = ::CreateFontIndirect(&m_lfDefault);
	
	//ENG: Remember a current context setting
	//RUS: ���������� ������� ��������� �������� ����������
	m_hOldFont = (HFONT)::SelectObject(m_hDC, m_hFont);
	m_nOldBkMode = ::SetBkMode(m_hDC, m_defStyle.nBkMode);
	m_crOldText = ::SetTextColor(m_hDC, m_defStyle.crText);
	m_crOldBk = ::SetBkColor(m_hDC, m_defStyle.crBkgnd);
	::GetTextMetrics(m_hDC, &m_tm);
	
	while (nIndex < m_csHtmlText.GetLength())
	{
		//ENG: Searching a begin of table
		//RUS: ���� ������ �������
		nBegin = nIndex;
		BOOL bFoundTable = SearchTag(m_csHtmlText, nIndex, _T("table"));

		//ENG: Gets a text before a table
		//RUS: �������� ����� �� �������
		strText = m_csHtmlText.Mid(nBegin, nIndex - nBegin);

		//ENG: If text before a table is exist
		//RUS: ���� ����� ����� �������� ����������
		if (!strText.IsEmpty())
		{
			//ENG: Add a tag BODY around of a output text
			//RUS: ��������� ��� BODY ������ ���������� ������
//			strText = _T("<body>") + strText + _T("</body>");

			//ENG: Output a text before of a table
			//RUS: ������� ����� ����� ��������
			szArea = DrawHtmlString(strText, &rcArea);

			//ENG: Updates a output area size
			//RUS: ��������� ������ ������� ������
			lpSize->cx = max(lpSize->cx, szArea.cx);
			lpSize->cy += szArea.cy;
			if (MODE_DRAW == m_nNumPass)
				rcArea.top += szArea.cy;
		} //if
		
		//ENG: If table was found
		//RUS: ���� ������� ���� �������
		if (bFoundTable)
		{
			//ENG: Searching an end of the table
			//RUS: ���� ��������� �������
			nBegin = nIndex;
			nIndex += 6;
			SearchEndOfTable(m_csHtmlText, nIndex);

			//ENG: Cuts a text of a table
			//RUS: �������� ����� �������
			strText = m_csHtmlText.Mid(nBegin, nIndex - nBegin);

			//ENG: Output a table
			//RUS: ����� �������
			szArea = DrawHtmlTable(strText, &rcArea);
			
			//ENG: Updates a output area size
			//RUS: ��������� ������ ������� ������
			lpSize->cx = max(lpSize->cx, szArea.cx);
			lpSize->cy += szArea.cy;
			if (MODE_DRAW == m_nNumPass)
				rcArea.top += szArea.cy;
		} //if
	} //while
	
	//ENG: Restore context setting
	//RUS: ��������������� �������� ��������� ����������
	::SetBkMode(m_hDC, m_nOldBkMode);
	::SetBkColor(m_hDC, m_crOldBk);
	::SetTextColor(m_hDC, m_crOldText);
	::SelectObject(m_hDC, m_hOldFont);
	
	//ENG: Clear stack of tags
	//RUS: ������� ���� �����
	m_arrStack.clear();
	
	//ENG: Delete a font
	//RUS: ������� �����
	::DeleteObject(m_hFont);
} //End of DrawHtml


SIZE CPPHtmlDrawer::DrawHtmlTable (CPPString & sTable, LPCRECT lpRect)
{
	//ENG: Jump to the next table
	//RUS: �������� ����� �������
	m_nCurTable++;

	int i;
	UINT pos;
	SIZE size = {0, 0};
	SIZE szTable;
	RECT rcTable = {0, 0, 0, 0};
	RECT rcRow;

	if (MODE_FIRSTPASS == m_nNumPass) 
	{
		//ENG: Get size of the table
		//RUS: �������� ������� �������
		szTable = GetTableDimensions(sTable);
		
		STRUCT_TABLE st;
		STRUCT_CELL sc;
		sc.nRowSpan = 0;
		sc.nColSpan = 0;
//		sc.bHeightPercent = FALSE;
//		sc.bWidthPercent = FALSE;
//		sc.nHeight = 0;
//		sc.nWidth = 0;
		sc.szText.cx = sc.szText.cy = sc.szCell.cx = sc.szCell.cy = 0;
		sc.bFixedWidth = FALSE;
	
		//ENG: Creates a template of an empty table
		//RUS: ������� ������ ������ �������
		vecRow rows;
		for (i = 0; i < szTable.cx; i++)
		{
			rows.push_back(sc);
			st.width.push_back(0);
			st.fixed_width.push_back(FALSE);
		} //for
		for (i = 0; i < szTable.cy; i++)
		{
			st.cells.push_back(rows);
			st.height.push_back(0);
		} //for
		
		//ENG: Add a new table
		//RUS: ��������� ����� �������
		m_arrTables.push_back(st);
	} //if

	//ENG: Gets an info about a current table
	//RUS: ����� ���������� � ������� ������� 
	int nIndexTable = m_nCurTable;
	STRUCT_TABLE cur_table = m_arrTables [nIndexTable];
	
	szTable.cx = cur_table.width.size();
	szTable.cy = cur_table.height.size();

	//ENG: Applies styles of <table> tag
	//RUS: ��������� ����� ������� (��� <table>)
	m_defStyle.strTag = _T("table");
	StoreRestoreStyle(FALSE);
	SelectNewHtmlStyle(m_defStyle.strTag, m_defStyle);
	
	//ENG: Passes a tag body and get a properties of the tag
	//RUS: ���������� ��� ������ ������ � �������� ������ ������� ����
	int nIndex = 0;
	CPPString sTag;
	SearchNextTag(sTable, sTag, nIndex);
	CPPString sProperties = SplitTag(sTag);

	//ENG: Analyses a properties of the tag
	//RUS: ����������� �������� ����
	AnalyseCellParam(sProperties, m_defStyle, TRUE);
	UpdateContext();

	if (MODE_FIRSTPASS != m_nNumPass)
	{
		//ENG: Gets a real size of the table
		//RUS: �������� �������� ������� �������
		rcTable.left = lpRect->left;
		rcTable.top = rcTable.bottom = lpRect->top;

		int nWidthTable = m_defStyle.nPadding + cur_table.width.size() - 1;
		for (pos = 0; pos < cur_table.width.size(); ++pos)
			nWidthTable += cur_table.width [pos] + m_defStyle.nPadding;
		rcTable.bottom += m_defStyle.nPadding + cur_table.height.size() - 1;
		for (pos = 0; pos < cur_table.height.size(); ++pos)
			rcTable.bottom += cur_table.height [pos] + m_defStyle.nPadding;

		if (CPPDrawManager::PEN_DOUBLE == m_defStyle.nBorderStyle)
		{
			nWidthTable += 6;
			rcTable.bottom += 6;
		}
		else
		{
			nWidthTable += m_defStyle.nBorderWidth * 2;
			rcTable.bottom += m_defStyle.nBorderWidth * 2;
		} //if

		//ENG: Horizontal align of the table
		//RUS: ������������ ������� �� �����������
		int nRealWidth = lpRect->right - lpRect->left;

		if (nWidthTable < nRealWidth)
		{
			//RUS: ���������� ��������� ������� �� ��� ��������� �������
			int nDelta = nRealWidth - nWidthTable;
			int nNotFixedColumns = 0;
			for (pos = 0; pos < cur_table.fixed_width.size(); ++pos)
			{
				if (!cur_table.fixed_width [pos])
					nNotFixedColumns++;
			} //for
			for (pos = 0; (pos < cur_table.fixed_width.size()) && (nNotFixedColumns > 0); ++pos)
			{
				if (!cur_table.fixed_width [pos])
				{
					int nStep = nDelta / nNotFixedColumns;
					cur_table.width [pos] += nStep;
					nDelta -= nStep;
					nNotFixedColumns--;
					nWidthTable += nStep;
				} //if
			} //for
		} //if

		if (nWidthTable < nRealWidth)
		{
			switch (m_defStyle.nHorzAlign)
			{
			case ALIGN_RIGHT:
				rcTable.left = lpRect->right - nWidthTable;
				break;
			case ALIGN_CENTER:
				rcTable.left += (nRealWidth - nWidthTable) / 2;
				break;
			} //switch
		} //if
		rcTable.right = rcTable.left + nWidthTable;

		//Calculate the real column's width and row's height
//		if (CPPDrawManager::PEN_DOUBLE == m_defStyle.nBorderStyle)
//			rcTable.bottom += m_defStyle.nBorderWidth * 6;
//		else
//			rcTable.bottom += m_defStyle.nBorderWidth * 2;
	} //if

	//Draw table border
	if (MODE_DRAW == m_nNumPass)
	{
		if (m_defStyle.nFillBkgnd >= 0)
		{
			m_drawmanager.FillEffect(m_hDC, m_defStyle.nFillBkgnd, &rcTable, 
				m_defStyle.crBkgnd, m_defStyle.crMidBkgnd, m_defStyle.crEndBkgnd,
				5);
		}
		else if (!m_defStyle.strNameResBk.IsEmpty())
		{
			DrawBackgroundImage(m_hDC, rcTable.left, rcTable.top, rcTable.right - rcTable.left, rcTable.bottom - rcTable.top, m_defStyle.strNameResBk);
		} //if
		if (m_defStyle.nBorderWidth > 0)
		{
			if (m_bIsEnable)
			{
				m_drawmanager.DrawRectangle(m_hDC, &rcTable, m_defStyle.crBorderLight, m_defStyle.crBorderDark,
					m_defStyle.nBorderStyle, m_defStyle.nBorderWidth);
			}
			else
			{
				m_drawmanager.DrawRectangle(m_hDC, &rcTable, m_crDisabled, m_crDisabled,
					m_defStyle.nBorderStyle, m_defStyle.nBorderWidth);
			} //if
		} //if
	} //if

	rcRow = rcTable;

	if (MODE_FIRSTPASS != m_nNumPass)
	{
		if (CPPDrawManager::PEN_DOUBLE == m_defStyle.nBorderStyle)
		{
			rcRow.left += 3;
			rcRow.top  += 3;
			rcRow.right -= 3;
			rcRow.bottom -= 3;
		}
		else
		{
			rcRow.left += m_defStyle.nBorderWidth;
			rcRow.top  += m_defStyle.nBorderWidth;
			rcRow.right -= m_defStyle.nBorderWidth;
			rcRow.bottom -= m_defStyle.nBorderWidth;
		}
	} //if

	if (szTable.cx && szTable.cy)
	{
		int nNewRow = 0;
		int nEndRow;
		CPPString sTagName, sTagParam, sRow;
		for (i = 0; i < szTable.cy; ++i)
		{
			//ENG: Searching a begin of the row
			//RUS: ����� ������ ������
			if (SearchTag(sTable, nNewRow, _T("tr")))
			{
				//ENG: The begin of the row was found. Searching end of the row
				//RUS: ������ ������ �������. ���� ��������� ������
				nEndRow = nNewRow;
				SearchEndOfRow(sTable, nEndRow);
				//ENG: The end of the row was found
				//RUS: ��������� ������ �������
				sRow = sTable.Mid(nNewRow, nEndRow - nNewRow);
				
				//ENG: Draw a row of the table
				//RUS: ������� ������ �������
				DrawHtmlTableRow(sRow, &rcRow, cur_table, i);
				
				//ENG: Jump to char after the end of the row
				//RUS: ������������ �� ������, ��������� �� ���������� ������
				nNewRow = nEndRow + 5;
			} //if
		} //for
	} //if

	if (MODE_DRAW != m_nNumPass)
	{
		//ENG: Analysing cell's width
		//RUS: ������ ������ ������
		for (i = 1; i <= szTable.cx; i++)
		{
			for (int y = 0; y < szTable.cy; y++)
			{
				vecRow & row = cur_table.cells [y];
				for (int x = 0; x < szTable.cx; x++)
				{
					STRUCT_CELL & sc = row [x];
					if (sc.nColSpan == i)
					{
						if (i == 1)
						{
							cur_table.width [x] = max (cur_table.width [x], sc.szCell.cx);
							if (sc.bFixedWidth)
								cur_table.fixed_width [x] = TRUE;
						}
						else
						{
							int span_width = 0;
							for (int z = 0; z < i; z++)
							{
								span_width += cur_table.width [x + z];
								if (sc.bFixedWidth)
									cur_table.fixed_width [x + z] = TRUE;
							} //for
							
							if (span_width < sc.szText.cx)
							{
								int step = (sc.szCell.cx - span_width) / i;
								cur_table.width [x + i - 1] += (sc.szCell.cx - span_width) % i;
								for (z = 0; z < i; z++)
									cur_table.width [x + z] += step;
							} //if
						} //if
					} //if
				} //for
			} //for
		} //for

		//ENG: Analysing cell's height
		//RUS: ������ ������ ������
		for (i = 1; i <= szTable.cy; i++)
		{
			for (int y = 0; y < szTable.cy; y++)
			{
				vecRow & row = cur_table.cells [y];
				for (int x = 0; x < szTable.cx; x++)
				{
					STRUCT_CELL & sc = row [x];
					if (sc.nRowSpan == i)
					{
						if (i == 1)
							cur_table.height [y] = max (cur_table.height [y], sc.szCell.cy);
						else
						{
							int span_height = 0;
							for (int z = 0; z < i; z++)
								span_height += cur_table.height [y + z];
							
							if (span_height < sc.szCell.cy)
							{
								int step = (sc.szCell.cy - span_height) / i;
								cur_table.height [y] += (sc.szCell.cy - span_height) % i;
								for (z = 0; z < i; z++)
									cur_table.height [y + z] += step;
							} //if
						} //if
					} //if
				} //for
			} //for
		} //for

		size.cx += m_defStyle.nPadding + szTable.cx - 1;
		size.cy += m_defStyle.nPadding + szTable.cy - 1;
		for (i = 0; i < szTable.cx; i++)
			size.cx += cur_table.width [i] + m_defStyle.nPadding;
		for (i = 0; i < szTable.cy; i++)
			size.cy += cur_table.height [i] + m_defStyle.nPadding;
		
		if (CPPDrawManager::PEN_DOUBLE == m_defStyle.nBorderStyle)
		{
			size.cx += m_defStyle.nBorderWidth * 6;
			size.cy += m_defStyle.nBorderWidth * 6;
		}
		else
		{
			size.cx += m_defStyle.nBorderWidth * 2;
			size.cy += m_defStyle.nBorderWidth * 2;
		} //if

//		size.cx = GetTableWidth(strTable, 0, size.cx, TRUE);
	}
	else
	{
		size.cx = rcTable.right - rcTable.left;
		size.cy = rcTable.bottom - rcTable.top;
	} //if

	//ENG: Stores a current table
	//RUS: ��������� ������� �������
	m_arrTables [nIndexTable] = cur_table;

	//ENG: Restore styles before <table> tag
	//RUS: ��������������� ����� �� ���� <table>
	m_defStyle.strTag = _T("table");
	if (StoreRestoreStyle(TRUE))
		UpdateContext();

	return size;
} //End DrawHtmlTable

///////////////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::DrawHtmlTableRow
//	Draw a row of the table
//-----------------------------------------------------------------------------
// Parameters:
//		sRow	- a text of the cell with the tags. For example: "<tr>...</tr>"
//		lpRect	- a bounding rectangle for the row
//		st		- the info about current table
//		nRow	- the current row of the table
///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::DrawHtmlTableRow(CPPString & sRow, LPCRECT lpRect, STRUCT_TABLE & st, int nRow)
{
	//ENG: Applies styles of <tr> tag
	//RUS: ��������� ����� ������ (��� <tr>)
	m_defStyle.strTag = _T("tr");
	StoreRestoreStyle(FALSE);
	SelectNewHtmlStyle(m_defStyle.strTag, m_defStyle);
	
	int nCol = 0;
	int i;
	vecRow & row = st.cells [nRow];
	
	//ENG: Passes a tag body and get a properties of the tag
	//RUS: ���������� ��� ������ ������ � �������� ������ ������� ����
	int nIndex = 0;
	CPPString sTag;
	SearchNextTag(sRow, sTag, nIndex);
	CPPString sProperties = SplitTag(sTag);

	//ENG: Analyses a properties of the tag
	//RUS: ����������� �������� ����
	AnalyseCellParam(sProperties, m_defStyle, FALSE);
	UpdateContext();
	
	while (nIndex < sRow.GetLength())
	{
		int nEndRow = nIndex;
		int nNewCell = nIndex;
		//ENG: Search an end of the cell or a begin of the nested table
		//RUS: ���� ����� ������ ��� ������ ��������� �������
		SearchTag(sRow, nEndRow, _T("/tr"));
		SearchTag(sRow, nNewCell, _T("td"));
		if (nNewCell < nEndRow)
		{
			//ENG: Search an existing cell
			//RUS: ����� ������������ ������
			STRUCT_CELL * sc2 = &row [nCol];
			while ((sc2->nColSpan < 0) && (nCol < (int)row.size())) 
			{
				nCol++;
				sc2 = &row [nCol];
			} //while
			STRUCT_CELL & sc = row [nCol];
			//ENG: Searching the end of the cell
			//RUS: ���� ��������� ������
			nIndex = nNewCell;
			SearchEndOfCell(sRow, nIndex);
			CPPString sCell = sRow.Mid(nNewCell, nIndex - nNewCell);

			RECT rcCell = {0, 0, 0, 0};
			if (MODE_FIRSTPASS != m_nNumPass)
			{
				//ENG: Gets a real rectangle to draw a cell
				//RUS: �������� �������� ������������� ��� ������ ������
				rcCell = *lpRect;
				rcCell.left += m_defStyle.nPadding;
				for (i = 0; i < nCol; i++)
					rcCell.left += st.width [i] + m_defStyle.nPadding + 1;
				rcCell.right = rcCell.left;
				for (i = 0; i < sc.nColSpan; i++)
					rcCell.right += st.width [nCol + i];
				rcCell.right += (sc.nColSpan - 1) * (m_defStyle.nPadding + 1);
				
				rcCell.top += m_defStyle.nPadding;
				for (i = 0; i < nRow; i++)
					rcCell.top += st.height [i] + m_defStyle.nPadding + 1;
				rcCell.bottom = rcCell.top;
				for (i = 0; i < sc.nRowSpan; i++)
					rcCell.bottom += st.height [nRow + i];
				rcCell.bottom += (sc.nRowSpan - 1) * (m_defStyle.nPadding + 1);

				//ENG: cellspacing - margins from table's edge to the cell's edge
				//RUS: cellspacing - ������ �� ������� ������� �� ������ 
//				rcCell.left += m_defStyle.nPadding;
//				rcCell.top += m_defStyle.nPadding;
//				rcCell.right -= m_defStyle.nPadding;
//				rcCell.bottom -= m_defStyle.nPadding;
			} //if

			DrawHtmlTableCell(sCell, &rcCell, sc);

			if (MODE_DRAW != m_nNumPass)
			{
				//ENG: Add a cellspacing
				//RUS: ��������� ������ ������ ��
//				sc.szCell.cx += m_defStyle.nPadding + m_defStyle.nPadding;
//				sc.szCell.cy += m_defStyle.nPadding + m_defStyle.nPadding;
				
				//ENG: Stores a span cells
				//RUS: ���������� ������������ ������
				int nColSpan = sc.nColSpan + nCol;
				int nRowSpan = sc.nRowSpan + nRow;
				for (i = nCol + 1; i < nColSpan; i++)
				{
					STRUCT_CELL & scTemp = row [i];
					scTemp.nColSpan = -1;
					scTemp.nRowSpan = -1;
				} //for
				for (i = nRow + 1; i < nRowSpan; i++)
				{
					vecRow & rowTemp = st.cells [i];
					STRUCT_CELL & scTemp = rowTemp [nCol];
					scTemp.nColSpan = -1;
					scTemp.nRowSpan = -1;
				} //for
			} //if
			nCol += sc.nColSpan;
		}
		else
		{
			nIndex = sRow.GetLength();
		} //if
	} //while

	//ENG: Restore styles before <tr> tag
	//RUS: ��������������� ����� �� ���� <tr>
	m_defStyle.strTag = _T("tr");
	if (StoreRestoreStyle(TRUE))
		UpdateContext();

} //End of DrawHtmlTableRow

///////////////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::DrawHtmlTableCell
//	Draw a table's cell
//-----------------------------------------------------------------------------
// Parameters:
//		sCell	- a text of the cell with the tags. For example: "<td>...</td>"
//		lpRect	- a bounding rectangle for cell
//		sc		- the info about current cell
///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::DrawHtmlTableCell(CPPString & sCell, LPCRECT lpRect, STRUCT_CELL & sc)
{
	if (MODE_DRAW != m_nNumPass)
	{
		sc.szText.cx = 0;
		sc.szText.cy = 0;
	} //if
	
	RECT rcCell = *lpRect;
	RECT rcText;

	//ENG: Applies styles of <td> tag
	//RUS: ��������� ����� ������ (��� <td>)
	m_defStyle.strTag = _T("td");
	StoreRestoreStyle(FALSE);
	SelectNewHtmlStyle(m_defStyle.strTag, m_defStyle);

	//ENG: Passes a tag body and get a properties of the tag
	//RUS: ���������� ��� ������ ������ � �������� ������ ������� ����
	int nIndex = 0;
	CPPString sTag;
	SearchNextTag(sCell, sTag, nIndex);
	CPPString sProperties = SplitTag(sTag);

	//ENG: Analyses a properties of the tag
	//RUS: ����������� �������� ����
	m_defStyle.nCellWidth = m_defStyle.nCellHeight = 0;
	m_defStyle.bCellWidthPercent = m_defStyle.bCellHeightPercent = FALSE;
	SIZE szSpan = AnalyseCellParam(sProperties, m_defStyle, FALSE);

	if (MODE_FIRSTPASS == m_nNumPass)
	{
		//ENG: Stores a cell span info
		//RUS: ��������� ���������� �� ����������� �����
		sc.nColSpan = szSpan.cx;
		sc.nRowSpan = szSpan.cy;
		//ENG: Stores an info about the recommended cell sizes
		//RUS: ��������� ���������� �� ��������������� �������� ������
//		sc.nWidth = m_defStyle.nCellWidth;
//		sc.bWidthPercent = m_defStyle.bCellWidthPercent;
//		sc.nHeight = m_defStyle.nCellHeight;
//		sc.bHeightPercent = m_defStyle.bCellHeightPercent;
		//ENG: 
		//RUS: ���� ������� ����������� �a����� ������, �� ���������� �� ��� ���������
		sc.szText.cx = m_defStyle.nCellWidth;
//		sc.szText.cy = m_defStyle.nCellHeight;
		sc.szText.cy = 0;

		if (m_defStyle.nCellWidth > 0)
			sc.bFixedWidth = TRUE;

		rcText = rcCell;
		rcText.right = rcText.left + sc.szText.cx;
		rcText.bottom = rcText.top + sc.szText.cy;
	}
	else if (MODE_DRAW == m_nNumPass)
	{
		//ENG: cellspacing - margins from table's edge to the cell's edge
		//RUS: cellspacing - ������ �� ������� ������� �� ������ 
		rcText = rcCell;

		if (m_defStyle.nFillBkgnd >= 0)
		{
			//ENG: Filling cell background
			//RUS: ���������� ���� ������
			m_drawmanager.FillEffect(m_hDC, m_defStyle.nFillBkgnd, &rcText, 
				m_defStyle.crBkgnd, m_defStyle.crMidBkgnd, m_defStyle.crEndBkgnd, 5);
		} //if
		
		//Draws the border
		if (m_bIsEnable) 
			m_drawmanager.DrawRectangle(m_hDC, &rcText, m_defStyle.crBorderDark, m_defStyle.crBorderLight, m_defStyle.nBorderStyle);
		else 
			m_drawmanager.DrawRectangle(m_hDC, &rcText, m_crDisabled, m_crDisabled, m_defStyle.nBorderStyle);
		
		//ENG: cellpadding - margin from cell's edge to the inside cell text
		//RUS: cellpadding - ������ �� ������� ������, �� ������ ������ ��
		rcText.left += m_defStyle.nMargin + m_defStyle.nBorderWidth;
		rcText.top += m_defStyle.nMargin + m_defStyle.nBorderWidth;
		rcText.right -= m_defStyle.nMargin + m_defStyle.nBorderWidth;
		rcText.bottom -= m_defStyle.nMargin + m_defStyle.nBorderWidth;
		
		//Vertical align
		switch (m_defStyle.nVertAlign)
		{
		case ALIGN_BOTTOM:
			rcText.top = rcText.bottom - sc.szText.cy;
			break;
		case ALIGN_VCENTER:
			rcText.top += (rcText.bottom - rcText.top - sc.szText.cy) / 2;
			break;
		} //switch
	} //if

	//ENG: Draws a cell
	//RUS: ����� ������
	while(nIndex < sCell.GetLength())
	{
		int nEndCell = nIndex;
		int nNewTable = nIndex;
		//ENG: Search an end of the cell or a begin of the nested table
		//RUS: ���� ����� ������ ��� ������ ��������� �������
		SearchTag(sCell, nEndCell, _T("/td"));
		SearchTag(sCell, nNewTable, _T("table"));
		//ENG: Gets a nearly index of the tag
		//RUS: �������� ������ ���������� ����
		int nNearlyTag = min(nEndCell, nNewTable);
		SIZE szTemp = {0, 0};
		if (nNearlyTag > nIndex)
		{
			//ENG: If between the last index and the current index there is a text
			//RUS: ���� ����� ��������� �������� � ������� �������� ���������� �����
			CPPString sText = sCell.Mid(nIndex, nNearlyTag - nIndex);
			szTemp = DrawHtmlString(sText, &rcText);
			nIndex = nNearlyTag;
		} //if
		else if (nNewTable < nEndCell)
		{
			//ENG: A nested table was found
			//RUS: ������� ��������� �������
			nIndex = nNewTable;
			SearchEndOfTable(sCell, nIndex);
			CPPString sTable = sCell.Mid(nNewTable, nIndex - nNewTable);
			szTemp = DrawHtmlTable(sTable, &rcText); 
		}
		else
		{
			//ENG: Alas, it is the end of the cell
			//RUS: ����� ������
			nIndex = sCell.GetLength();
		} //if
		
		if (MODE_DRAW != m_nNumPass)
		{
			//ENG: On first and second passes we are calculate the dimensions of the cell
			//RUS: �� ������ � ������ �������� ��������� ������� ������.
			sc.szText.cx = max(szTemp.cx, sc.szText.cx);
			sc.szText.cy += szTemp.cy;
		} //if
		rcText.top += szTemp.cy;
	} //while

	if (MODE_DRAW != m_nNumPass)
	{
		//ENG: On first and second passes we are calculate the dimensions of the cell
		//RUS: �� ������ � ������ �������� ��������� ������� ������.
		sc.szCell.cx = max(m_defStyle.nCellWidth, sc.szText.cx);
		sc.szCell.cy = max(m_defStyle.nCellHeight, sc.szText.cy);

		//ENG: Add the margins of the text from the cell's edges
		//RUS: ��������� ������� ������ �� ������ ������
		sc.szCell.cx += 2 * (m_defStyle.nMargin + m_defStyle.nBorderWidth);
		sc.szCell.cy += 2 * (m_defStyle.nMargin + m_defStyle.nBorderWidth);
	} //if
		
	//ENG: Restore styles before <td> tag
	//RUS: ��������������� �����, ������� ���� �� ���� <td>
	m_defStyle.strTag = _T("td");
	if (StoreRestoreStyle(TRUE))
		UpdateContext();
}

SIZE CPPHtmlDrawer::DrawHtmlString (CPPString & sHtml, LPCRECT lpRect)
{
	SIZE szTextArea = {0, 0};

	COLORREF clrShadow = m_bIsEnable ? m_crShadow : GetColorByName("");

	//ENG: For any string we are add a <body> tag as wrapper
	//RUS: ��� ����� ������ ��������� ��� <body>
	sHtml = _T("<body>") + sHtml;
	sHtml += _T("</body>");

	//ENG: Bounding rectangle for a full text
	//RUS: �������������� ������������� ��� ������ ����� ������
	m_rcOutput.top = lpRect->top;
	m_rcOutput.left = lpRect->left;
	m_rcOutput.bottom = lpRect->bottom;
	m_rcOutput.right = lpRect->right;

	//ENG: The width of the bounding rectangle
	//RUS: ������ ��������������� ��������������
	int nTextWrapWidth = m_rcOutput.right - m_rcOutput.left;

	//ENG: A current position for output
	//RUS: ������� ������� ��� ������
	POINT ptOutput;
	ptOutput.x = lpRect->left;
	ptOutput.y = lpRect->top;

//	szTextArea.cx = szTextArea.cy = 0;
//	m_szOutput.cx = m_szOutput.cy = 0;

//	m_szOutput = CSize(0, 0);

	//ENG: If a text is empty
	//RUS: ���� ������ ��� ������ ���
//	if (str.IsEmpty())
//	{
//		szTextArea.cx = szTextArea.cy = 0;
//		return;
//	} //if

	int nFirstLine = m_nCurLine;

//	POINT pt;
//	pt.x = lpRect->left;
//	pt.y = lpRect->top;

	int y;
	SIZE sz;

	CPPString sText = _T("");
	CPPString sTag = _T(""); //String of the tag
	CPPString sProperties = _T(""); //String of the tag's property
	CPPString sParameter = _T("");
	CPPString sValue = _T("");

	BOOL bCloseTag = FALSE; //TRUE if tag have symbol '\'

	//ENG: Initializing a new line
	//RUS: ������������� ����� ������
	ptOutput.x = InitNewLine(ptOutput.x);
	int nBeginLineX = ptOutput.x;
	int nSpacesInLine = m_hline.nSpaceChars;
	int nRealWidth = m_hline.nWidthLine;

	int nIndex = 0;
	int nBegin = 0;
	int i = 0;
	while (i < sHtml.GetLength())
	{
		//ENG: Searching a first tag
		//RUS: ����� ������� ����
		sText = SearchNextTag(sHtml, sTag, i);
		sProperties = SplitTag(sTag);

		//ENG: Before a tag was exist a text
		//RUS: ����� ����� ���� ����� ��� ������
		if (!sText.IsEmpty())
		{
			//ENG: Transform text
			//RUS: ����������� �����
			switch (m_defStyle.nTextTransform)
			{
			case TEXT_TRANSFORM_UPPERCASE:
				//ENG: All chars make upper
				//RUS: ��� ������� ��������� � ������� �������
				sText.MakeUpper();
				break;
			case TEXT_TRANSFORM_LOWERCASE:
				//ENG: All chars make lower
				//RUS: ��� ������� ��������� � ������ �������
				sText.MakeLower();
				break;
			case TEXT_TRANSFORM_CAPITALIZE:
				//ENG: Each first char of a word to upper
				//RUS: ����� ������ ������ ����� � ������� �������, ��������� � ������
				sText.MakeLower();
				for (nIndex = 0; nIndex < sText.GetLength(); nIndex++)
				{
					if ((sText.GetAt(nIndex) >= _T('a')) && (sText.GetAt(nIndex) <= _T('z')))
					{
						if ((0 == nIndex) || (_T(' ') == sText.GetAt(nIndex - 1)))
							sText.SetAt(nIndex, sText.GetAt(nIndex) - _T('a') + _T('A'));
					} //if
				} //if
				break;
			} //switch

			//RUS: ����������� �� ��� ���, ���� �� ����� ������� ���� �����
			while (!sText.IsEmpty())
			{
				//ENG: Reset an additional interval for space chars
				//RUS: ����� ��������������� ��������� ����� �������
				::SetTextJustification(m_hDC, 0, 0);

				//ENG: Gets a size a output text
				//RUS: �������� ������ ���������� ������
				::GetTextExtentPoint32(m_hDC, sText, sText.GetLength(), &sz);

				//ENG: Gets a real top coordinate to output with vertical alignment
				//RUS: �������� �������� ��������� ����� ������ � ������ ������������� ������������
				y = VerticalAlignText(ptOutput.y, sz.cy);

				CPPString sTemp = sText;
				int nMaxSize = nTextWrapWidth - ptOutput.x + m_rcOutput.left;

				if (m_nMaxWidth && ((nMaxSize - sz.cx) < 0) && nTextWrapWidth)
				{
					//ENG: Text wrap was enabled and text out for a bounding rectangle
					int nRealSize = nMaxSize;
					sTemp = GetWordWrap(sText, nTextWrapWidth, nRealSize);
					sz.cx = nRealSize;
				}
				else
				{
					sText.Empty();
				} //if

				if (MODE_DRAW == m_nNumPass)
				{
					if (sz.cx)
					{
						if ((0 == (nRealWidth - sz.cx)) && (_T(' ') == sTemp.GetAt(sTemp.GetLength() - 1)))
						{
							//ENG: Removes the right space chars for the last output in line
							//RUS: ���� ��� ��������� ����� � ������, �� ������� ������� ������
							sTemp.TrimRight();
							nSpacesInLine = GetCountOfChars(sTemp);
							SIZE szTemp;
							::GetTextExtentPoint32(m_hDC, sTemp, sTemp.GetLength(), &szTemp);
							nRealWidth -= (sz.cx - szTemp.cx);
						} //if

						if ((ALIGN_JUSTIFY == m_hline.nHorzAlign) && m_hline.bWrappedLine)
							::SetTextJustification(m_hDC, nMaxSize - nRealWidth, nSpacesInLine);
						nRealWidth -= sz.cx;
						
						//ENG: Gets a size a output text
						//RUS: �������� ������ ���������� ������
						::GetTextExtentPoint32(m_hDC, sTemp, sTemp.GetLength(), &sz);
						
						//ENG: Stores a current area as a hyperlink area if it available
						//RUS: ��������� ������� ������� ��� ������� ���������� ���� �� ����������
						StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + sz.cx, y + sz.cy);
						
						//ENG: Real output a text
						//RUS: ����� ������
						::TextOut(m_hDC, ptOutput.x, y, sTemp, sTemp.GetLength());
						nSpacesInLine -= GetCountOfChars(sTemp);
						
						//ENG: If sets an overline style then draw a line over the text
						//RUS: ���� ���������� ����� overline, �� ������ ����� ��� �������
						if (m_defStyle.bOverlineFont)
						{
							HPEN hpenOverline = ::CreatePen(PS_SOLID, (m_defStyle.nWeightFont >= FW_BOLD) ? 2 : 1, m_defStyle.crText);
							HPEN hOldPen = (HPEN)::SelectObject(m_hDC, hpenOverline);
							::MoveToEx(m_hDC, ptOutput.x, y, NULL);
							::LineTo(m_hDC, ptOutput.x + sz.cx, y);
							::SelectObject(m_hDC, hOldPen);
						} //if
					} //if
				}
				else
				{
					//ENG: Stores a last horizontal alignment
					//RUS: ��������� ��������� �������������� ������������
					m_hline.nHorzAlign = m_defStyle.nHorzAlign;

					//ENG:
					//RUS:
					m_hline.nSpaceChars += GetCountOfChars(sTemp);
				} //if

				//ENG: Moves to a right of the outputed text
				//RUS: ������������ ������ �� ����������� ������
				ptOutput.x += sz.cx;
				if (!sText.IsEmpty())
				{
					//ENG: Not all text was printed (cause text wrap) 
					//RUS: �� ��� ������ ��� �������� (� ������ �������� ������)
					m_hline.bWrappedLine = TRUE;
					Tag_NewLine(&ptOutput, 1, &szTextArea);
					nBeginLineX = ptOutput.x;
					nSpacesInLine = m_hline.nSpaceChars;
					nRealWidth = m_hline.nWidthLine;
				}
			} //while
		} //if

		//ENG: If tag was found then analyzing ...
		//RUS: ���� ��� ������, ����������� ...
		if (!sTag.IsEmpty())
		{
			//ENG: Reset temporary parameters
			//RUS: ����� ��������� ����������
			m_defStyle.strTag.Empty();
			bCloseTag = FALSE;
			
			//ENG: Get Tag's name
			//RUS: �������� ��� ����
			nIndex = 0;
			
			//ENG: Searching a tag's value
			//RUS: ����� �������� ����
			DWORD dwTag = GetTagFromList(sTag, m_defStyle.strTag, bCloseTag);
			
			//ENG: If a tag was found in a list of the tags
			//RUS: ���� ��� ������ � ������
			if (TAG_NONE != dwTag)
			{
				//ENG: If it is a style tag 
				//RUS: ���� ������� ��� ��� ������ �� �������
				if (!m_defStyle.strTag.IsEmpty())
				{
					//ENG: Checks on permissibility of tag
					//RUS: ��������� �� ������������ ����
					if (StoreRestoreStyle(bCloseTag))
					{
						//ENG: If it isn't a close tag
						//RUS: ���� ��� �� ��������� ����
						if (!bCloseTag)
						{
							//ENG: Processing a tag
							//RUS: ��������� ����
							switch (dwTag)
							{
							case TAG_BOLD:
								m_defStyle.nWeightFont <<= 1;
								if (m_defStyle.nWeightFont > FW_BLACK)
									m_defStyle.nWeightFont = FW_BLACK;
								break;
							case TAG_ITALIC:
								m_defStyle.bItalicFont = m_defStyle.bItalicFont ? FALSE : TRUE;
								break;
							case TAG_UNDERLINE:
								m_defStyle.bUnderlineFont = m_defStyle.bUnderlineFont ? FALSE : TRUE;
								break;
							case TAG_STRIKEOUT:
								m_defStyle.bStrikeOutFont = m_defStyle.bStrikeOutFont ? FALSE : TRUE;
								break;
							case TAG_FONT:
								//Search parameters
								while (nIndex < sProperties.GetLength())
								{
									//ENG: Searching a parameters of a tag
									//RUS: ����� ���������� ����
									sValue = GetNextProperty(sProperties, nIndex, sParameter);
									//ENG: If a parameter was found
									//RUS: ���� �������� ������
									if (!sParameter.IsEmpty())
									{
										//ENG: Processing a parameters of a tag
										//RUS: ��������� ���������� ����
										if (sParameter == _T("face"))
											m_defStyle.sFaceFont = GetStyleString(sValue, m_defStyle.sFaceFont);
										else if (sParameter == _T("size"))
											m_defStyle.nSizeFont = GetLengthUnit(sValue, m_defStyle.nSizeFont, TRUE);
										else if (sParameter == _T("color"))
										{
											if (m_bIsEnable)
												m_defStyle.crText = GetStyleColor(sValue, m_defStyle.crText);
											else
												m_defStyle.crText = GetColorByName("");
										}
										else if (sParameter == _T("style"))
											GetStyleFontShortForm(sValue);
										else if (sParameter == _T("weight"))
											m_defStyle.nWeightFont = GetStyleFontWeight(sValue, m_defStyle.nWeightFont);
										else if (sParameter == _T("bkgnd"))
										{
											if (((sValue == _T("transparent")) && sValue.IsEmpty()) || !m_bIsEnable)
											{
												m_defStyle.nBkMode = TRANSPARENT;
											}
											else
											{
												m_defStyle.nBkMode = OPAQUE;
												m_defStyle.crBkgnd = GetStyleColor(sValue, m_defStyle.crBkgnd);
											} //if
										} //if
									} //if
								} //while
								break;
							case TAG_LEFT:
								m_defStyle.nHorzAlign = ALIGN_LEFT;
								break;
							case TAG_CENTER:
								m_defStyle.nHorzAlign = ALIGN_CENTER;
								break;
							case TAG_RIGHT:
								m_defStyle.nHorzAlign = ALIGN_RIGHT;
								break;
							case TAG_JUSTIFY:
								m_defStyle.nHorzAlign = ALIGN_JUSTIFY;
								break;
							case TAG_BASELINE:
								m_defStyle.nVertAlign = ALIGN_BASELINE;
								break;
							case TAG_TOP:
								m_defStyle.nVertAlign = ALIGN_TOP;
								break;
							case TAG_VCENTER:
								m_defStyle.nVertAlign = ALIGN_VCENTER;
								break;
							case TAG_BOTTOM:
								m_defStyle.nVertAlign = ALIGN_BOTTOM;
								break;
							case TAG_NEWSTYLE:
								SelectNewHtmlStyle(sTag, m_defStyle);
								break;
							case TAG_SPAN:
								while (nIndex < sProperties.GetLength())
								{
									//ENG: Searching a parameters of a tag
									//RUS: ����� ���������� ����
									sValue = GetNextProperty(sProperties, nIndex, sParameter);
									//ENG: If a parameter was found
									//RUS: ���� �������� ������
									if (sParameter == _T("class"))
										SelectNewHtmlStyle(_T(".") + GetStyleString(sValue, _T("")), m_defStyle);
								} //while
								break;
							case TAG_HYPERLINK:
								//ENG: A default values
								//RUS: �������� �� ���������
								m_defStyle.nTypeLink = LINK_MESSAGE;
								m_defStyle.sHyperlink.Empty();
								while (nIndex < sProperties.GetLength())
								{
									//ENG: Searching a parameters of a tag
									//RUS: ����� ���������� ����
									sValue = GetNextProperty(sProperties, nIndex, sParameter);
									//ENG: If a parameter was found
									//RUS: ���� �������� ������
									if (!sParameter.IsEmpty())
									{
										//ENG: Processing a parameters of a tag
										//RUS: ��������� ���������� ����
										if (sParameter == _T("href"))
										{
											m_defStyle.nTypeLink = LINK_HREF;
											m_defStyle.sHyperlink = GetStyleString(sValue, _T(""));
										} //if
										if (sParameter == _T("msg"))
										{
											m_defStyle.nTypeLink = LINK_MESSAGE;
											m_defStyle.sHyperlink = GetStyleString(sValue, _T(""));
										} //if
									} //if
								} //while
								//ENG: Gets a index of a current link
								//RUS: �������� ������ ������� �����������
								m_nCurIndexLink ++;
								//ENG: If a mouse over this link
								//RUS: ���� ��� ��� ���� �����
								if (m_nCurIndexLink == m_nHoverIndexLink)
									SelectNewHtmlStyle(_T("a:hover"), m_defStyle);
								else
									SelectNewHtmlStyle(_T("a:link"), m_defStyle);
								break;
								} //switch
							} //if
							//ENG: Update a device context
							//RUS: ���������� ��������� ����������
							UpdateContext();
						} //if
					}
					else 
					{
						BOOL bPercent;
						BOOL bShadow;
						BOOL bAutoDelete;
						int nWidth, nNum;
						
						STRUCT_IMAGE si;
						STRUCT_CHANGESTYLE csTemp; //Temporary structure
						STRUCT_ANIMATION sa;
						
						SIZE szReal;
						HBITMAP hBitmap = NULL;;
						HICON hIcon = NULL;
						
						DWORD nMaxCol, nMaxRow;
						UINT nIdRes, nIdDll;
						//CPPString str;
						
						//ENG: Processing a tag
						//RUS: ��������� ����
						switch (dwTag)
						{
						case TAG_HLINE:
							//ENG: Draws the horizontal line
							//RUS: ��������� �������������� �����
							csTemp = m_defStyle;
							csTemp.nBorderWidth = 1;
							//ENG: Applies a new styles for <hr> tag
							SelectNewHtmlStyle(_T("hr"), csTemp);
							nWidth = 100;
							bPercent = TRUE;
							
							while (nIndex < sProperties.GetLength())
							{
								//ENG: Searching a parameters of a tag
								//RUS: ����� ���������� ����
								sValue = GetNextProperty(sProperties, nIndex, sParameter);
								//ENG: If a parameter was found
								//RUS: ���� �������� ������
								if (!sParameter.IsEmpty())
								{
									//ENG: Processing a parameters of a tag
									//RUS: ��������� ���������� ����
									if (sParameter == _T("width"))
									{
										bPercent = IsPercentableValue(sValue);
										nWidth = GetLengthUnit(sValue, 100);
									}
									else if (sParameter == _T("size"))
										csTemp.nBorderWidth = GetLengthUnit(sValue, csTemp.nBorderWidth);
									else if (sParameter == _T("color"))
									{
										if (m_bIsEnable)
											csTemp.crText = GetStyleColor(sValue, csTemp.crText);
										else
											csTemp.crText = GetColorByName("");
									}
								} //if
							} //while
							
							if (bPercent)
							{
								if (MODE_FIRSTPASS == m_nNumPass)
								{
									m_hline.nAddPercentWidth += nWidth;
									nWidth = 1;
								}
								else nWidth = ::MulDiv(lpRect->right - lpRect->left, nWidth, 100);
							} //if
							
							if (MODE_FIRSTPASS == m_nNumPass)
							{
								m_hline.nHeightLine = max(m_hline.nHeightLine, csTemp.nBorderWidth + 8);
								m_hline.nHorzAlign = m_defStyle.nHorzAlign; //Store a last horizontal alignment
							}
							else if (MODE_DRAW == m_nNumPass)
							{
								m_drawmanager.DrawLine(m_hDC, ptOutput.x, ptOutput.y + m_hline.nHeightLine / 2, 
									ptOutput.x + nWidth, ptOutput.y + m_hline.nHeightLine / 2, 
									csTemp.crText, CPPDrawManager::PEN_SOLID, csTemp.nBorderWidth);
							} //if
							ptOutput.x += nWidth;
							break;
						case TAG_NEWLINE:
							//ENG: New line
							//RUS: ����� ������
							nNum = 1;
							if (!sProperties.IsEmpty())
							{
								sProperties = sProperties.Mid(1);
								nNum = GetLengthUnit(sProperties, nNum);
							} //if
							m_hline.bWrappedLine = FALSE;
							Tag_NewLine(&ptOutput, nNum, &szTextArea);
							nBeginLineX = ptOutput.x;
							nSpacesInLine = m_hline.nSpaceChars;
							nRealWidth = m_hline.nWidthLine;
							break;
						case TAG_TABULATION:
							//ENG: Tabulation
							//RUS: ���������
							nNum = 1;
							if (!sProperties.IsEmpty())
							{
								sProperties = sProperties.Mid(1);
								nNum = GetLengthUnit(sProperties, nNum);
							} //if
							Tag_Tabulation(&ptOutput, nNum);
							break;
						case TAG_BITMAP:
							//-----------------------------
							//Draws the bitmap 
							//-----------------------------
							//ENG: Default Parameters
							//RUS: ��������� �� ���������
							si.nIdRes = 0;
							si.nIdDll = 0;
							si.nHandle = 0;
							si.nWidth = 100;
							si.bPercentWidth = TRUE;
							si.nHeight = 100;
							si.bPercentHeight = TRUE;
							si.crMask = RGB(255, 0, 255);
							si.bUseMask = FALSE;
							si.nStyles = 0;
							si.nHotStyles = 0;
							si.strSrcFile.Empty();
							si.strPathDll.Empty();
							
							//ENG: Searching image parameters
							//RUS: ����� ���������� �����������
							AnalyseImageParam(sProperties, si);
							
							//ENG: If a image's source was specified
							//RUS: ���� ������ �������� �����������
							if (si.nIdRes || si.nIdDll || si.nHandle || !si.strSrcFile.IsEmpty())
							{
								//ENG: Sets a autodelete flag of the image object
								//RUS: ���������� ���� ��������������� �������� ������� �����������
								bAutoDelete = TRUE;
								
								//ENG: Gets a handle of the image
								//RUS: �������� ���������� �����������
								if (si.nIdRes)
									hBitmap = GetBitmapFromResources(si.nIdRes);
								else if (!si.strSrcFile.IsEmpty())
									hBitmap = GetBitmapFromFile(si.strSrcFile);
								else if (si.nIdDll)
									hBitmap = GetBitmapFromDll(si.nIdDll, si.strPathDll);
								else if (si.nHandle)
								{
									hBitmap = (HBITMAP)si.nHandle;
									//ENG: If an image handle specified, disables autodelete
									//RUS: ���� ������ ���������� �����������, �� ��������� ��������
									bAutoDelete = FALSE;
								} //if
								
								//ENG: If a handle of an image was retrieved
								//RUS: ���� ���������� ����������� �������
								if (NULL != hBitmap)
								{
									//ENG: Image with shadow or not?
									//RUS: ����������� � ����� ��� ���
									bShadow = IsImageWithShadow(si);
									
									//ENG: Retrieves an original size of an image
									//RUS: �������� ������������ ������ �����������
									m_drawmanager.GetSizeOfBitmap(hBitmap, &sz);
									
									//ENG: Retrieves an output size
									//RUS: �������� ������� ��� ���������
									if (si.bPercentWidth) si.nWidth = ::MulDiv(sz.cx, si.nWidth, 100);
									if (si.bPercentHeight) si.nHeight = ::MulDiv(sz.cy, si.nHeight, 100);
									
									//ENG: If a shadow was enabled then set a real size
									//RUS: ���� ���� ��������, �� ������������� �������� ������
									if (si.nWidth && si.nHeight && bShadow)
									{
										sz.cx = si.nWidth + m_szOffsetShadow.cx;
										sz.cy = si.nHeight + m_szOffsetShadow.cy;
									} //if
									
									int nMaxSize = nTextWrapWidth - ptOutput.x + m_rcOutput.left;
									if (m_nMaxWidth && ((nMaxSize - sz.cx) < 0) && nTextWrapWidth) 
									{
										//ENG: Not all text was printed (cause text wrap) 
										//RUS: �� ��� ������ ��� �������� (� ������ �������� ������)
										m_hline.bWrappedLine = TRUE;
										Tag_NewLine(&ptOutput, 1, &szTextArea);
										nBeginLineX = ptOutput.x;
										nSpacesInLine = m_hline.nSpaceChars;
										nRealWidth = m_hline.nWidthLine;
									} //if
									nRealWidth -= sz.cx;

									//ENG: Store a last horizontal alignment
									//RUS: ���������� ��������� �������������� ������������
									if (MODE_FIRSTPASS == m_nNumPass) 
										m_hline.nHorzAlign = m_defStyle.nHorzAlign;
									
									//ENG: Retrieves a vertical coordinates of drawing area
									//RUS: �������� ������������ ���������� ������� ���������
									y = VerticalAlignImage(ptOutput.y, si.nHeight);
									
									//ENG: If an image is exist and not prepare mode
									//RUS: ���� ����������� �������� � �� ���������� ����� ����������
									if (si.nWidth && si.nHeight && (MODE_DRAW == m_nNumPass))
									{
										//ENG: Add an output area to hyperlink list if needed
										//RUS: ���� ���������� ��������� ������� ������ � ������ �����������
										StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + sz.cx, y + sz.cy);
										
										//ENG: If a mouse over an image then applies a hot styles
										//RUS: ���� ���� ��� ������������, �� ��������� �������������� �����
										if (m_defStyle.nTypeLink != LINK_NONE)
										{
											if (m_nCurIndexLink == m_nHoverIndexLink)
												si.nStyles = si.nHotStyles;
										} //if
										
										if (!m_bIsEnable)
											si.nStyles = (si.nStyles & 0xFF00) | IMAGE_EFFECT_MONOCHROME;
										
										//ENG: Drawing an image
										//RUS: ��������� �����������
										m_drawmanager.DrawBitmap(m_hDC, ptOutput.x, y, si.nWidth, si.nHeight, hBitmap, 
											si.bUseMask, si.crMask, si.nStyles, 
											bShadow, 
											m_szOffsetShadow.cx, m_szOffsetShadow.cy, 
											m_szDepthShadow.cx, m_szDepthShadow.cy, 
											clrShadow);
									} //if
									
									//ENG: Moves to a right of the outputed image
									//RUS: ������������ ������ �� ����������� �����������
									ptOutput.x += sz.cx; //si.nWidth;
									
									//ENG: If needed delete a handle of an image
									//RUS: ���� ���������� ������� ���������� �����������
									if (bAutoDelete)
										::DeleteObject(hBitmap);
								} //if
							} //if
							break;
						case TAG_ICON:
							//-----------------------------
							//Draws the icon
							//-----------------------------
							//ENG: Default Parameters
							//RUS: ��������� �� ���������
							si.nIdRes = 0;
							si.nIdDll = 0;
							si.nHandle = 0;
							si.nWidth = 100;
							si.bPercentWidth = TRUE;
							si.nHeight = 100;
							si.bPercentHeight = TRUE;
							si.nStyles = 0;
							si.nHotStyles = 0;
							si.strSrcFile.Empty();
							si.strPathDll.Empty();
							
							//ENG: Searching image parameters
							//RUS: ����� ���������� �����������
							AnalyseImageParam(sProperties, si);
							
							//ENG: If a image's source was specified
							//RUS: ���� ������ �������� �����������
							if (si.nIdRes || si.nIdDll || si.nHandle || !si.strSrcFile.IsEmpty())
							{
								//ENG: Sets a autodelete flag of the image object
								//RUS: ���������� ���� ��������������� �������� ������� �����������
								bAutoDelete = TRUE;
								
								//RUS: �������� ��������� ������ ������
								sz.cx = si.nWidth;
								sz.cy = si.nHeight;
								if (si.bPercentWidth) sz.cx = ::MulDiv(::GetSystemMetrics(SM_CXICON), si.nWidth, 100);
								if (si.bPercentHeight) sz.cy = ::MulDiv(::GetSystemMetrics(SM_CYICON), si.nHeight, 100);
								
								//ENG: Gets a handle of the image
								//RUS: �������� ���������� �����������
								if (si.nIdRes)
									hIcon = GetIconFromResources(si.nIdRes, sz.cx, sz.cy);
								else if (!si.strSrcFile.IsEmpty())
									hIcon = GetIconFromFile(si.strSrcFile, sz.cx, sz.cy);
								else if (si.nIdDll)
									hIcon = GetIconFromDll(si.nIdDll, sz.cx, sz.cy, si.strPathDll);
								else if (si.nHandle)
								{
									hIcon = (HICON)si.nHandle;
									
									//ENG: If an image handle specified, disables autodelete
									//RUS: ���� ������ ���������� �����������, �� ��������� ��������
									bAutoDelete = FALSE;
								} //if
								
								//ENG: If a handle of an image was retrieved
								//RUS: ���� ���������� ����������� �������
								if (NULL != hIcon)
								{
									//ENG: Image with shadow or not?
									//RUS: ����������� � ����� ��� ���
									BOOL bShadow = IsImageWithShadow(si);
									
									//ENG: Retrieves an original size of an image
									//RUS: �������� ������������ ������ �����������
									m_drawmanager.GetSizeOfIcon(hIcon, &sz);
									si.nWidth = sz.cx;
									si.nHeight = sz.cy;
									
									//ENG: Retrieves an output size
									//RUS: �������� ������� ��� ���������
									//									if (si.bPercentWidth) si.nWidth = ::MulDiv(sz.cx, si.nWidth, 100);
									//									if (si.bPercentHeight) si.nHeight = ::MulDiv(sz.cy, si.nHeight, 100);
									
									//ENG: If a shadow was enabled then set a real size
									//RUS: ���� ���� ��������, �� ������������� �������� ������
									if (si.nWidth && si.nHeight && bShadow)
									{
										sz.cx = si.nWidth + m_szOffsetShadow.cx;
										sz.cy = si.nHeight + m_szOffsetShadow.cy;
									} //if

									int nMaxSize = nTextWrapWidth - ptOutput.x + m_rcOutput.left;
									if (m_nMaxWidth && ((nMaxSize - sz.cx) < 0) && nTextWrapWidth) 
									{
										//ENG: Not all text was printed (cause text wrap) 
										//RUS: �� ��� ������ ��� �������� (� ������ �������� ������)
										m_hline.bWrappedLine = TRUE;
										Tag_NewLine(&ptOutput, 1, &szTextArea);
										nBeginLineX = ptOutput.x;
										nSpacesInLine = m_hline.nSpaceChars;
										nRealWidth = m_hline.nWidthLine;
									} //if
									nRealWidth -= sz.cx;
									
									//ENG: Store a last horizontal alignment
									//RUS: ���������� ��������� �������������� ������������
									if (MODE_FIRSTPASS == m_nNumPass) 
										m_hline.nHorzAlign = m_defStyle.nHorzAlign;
									
									//ENG: Retrieves a vertical coordinates of drawing area
									//RUS: �������� ������������ ���������� ������� ���������
									y = VerticalAlignImage(ptOutput.y, si.nHeight);
									
									//ENG: If an image is exist and not prepare mode
									//RUS: ���� ����������� �������� � �� ���������� ����� ����������
									if (si.nWidth && si.nHeight && (MODE_DRAW == m_nNumPass))
									{
										//ENG: Add an output area to hyperlink list if needed
										//RUS: ���� ���������� ��������� ������� ������ � ������ �����������
										StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + sz.cx, y + sz.cy);
										
										//ENG: If a mouse over an image then applies a hot styles
										//RUS: ���� ���� ��� ������������, �� ��������� �������������� �����
										if (m_defStyle.nTypeLink != LINK_NONE)
										{
											if (m_nCurIndexLink == m_nHoverIndexLink)
												si.nStyles = si.nHotStyles;
										} //if
										
										if (!m_bIsEnable)
											si.nStyles = (si.nStyles & 0xFF00) | IMAGE_EFFECT_MONOCHROME;
										
										//ENG: Drawing an image
										//RUS: ��������� �����������
										m_drawmanager.DrawIcon(m_hDC, ptOutput.x, y, si.nWidth, si.nHeight, hIcon, si.nStyles, 
											bShadow, 
											m_szOffsetShadow.cx, m_szOffsetShadow.cy, 
											m_szDepthShadow.cx, m_szDepthShadow.cy, 
											clrShadow);
									} //if
									//ENG: Moves to a right of the outputed image
									//RUS: ������������ ������ �� ����������� �����������
									ptOutput.x += sz.cx; //si.nWidth;
									
									//ENG: If needed delete a handle of an image
									//RUS: ���� ���������� ������� ���������� �����������
									if (bAutoDelete) 
										::DestroyIcon(hIcon);
								} //if
							} //if
							break;
						case TAG_IMAGELIST:
							//-----------------------------
							//Draws the icon from image list
							//-----------------------------
							//ENG: Default Parameters
							//RUS: ��������� �� ���������
							si.nIndexImageList = 0;
							si.nIdRes = 0;
							si.nIdDll = 0;
							si.nHandle = 0;
							si.nWidth = 100;
							si.bPercentWidth = TRUE;
							si.nHeight = 100;
							si.bPercentHeight = TRUE;
							si.nSpeed = 0;
							si.bUseMask = FALSE;
							si.crMask = RGB(255, 0, 255);
							si.cx = 0;//GetSystemMetrics(SM_CXICON);
							si.cy = 0;//GetSystemMetrics(SM_CYICON);
							si.nStyles = 0;
							si.nHotStyles = 0;
							si.strSrcFile.Empty();
							si.strPathDll.Empty();
							
							//ENG: Searching image parameters
							//RUS: ����� ���������� �����������
							AnalyseImageParam(sProperties, si);
							
							//ENG: Image with shadow or not?
							//RUS: ����������� � ����� ��� ���
							bShadow = IsImageWithShadow(si);
							
							if (si.nIdRes || si.nIdDll || si.nHandle || !si.strSrcFile.IsEmpty())
							{
								//ENG: Sets a autodelete flag of the image object
								//RUS: ���������� ���� ��������������� �������� ������� �����������
								bAutoDelete = TRUE;
								
								//ENG: Gets a handle of the image
								//RUS: �������� ���������� �����������
								if (si.nIdRes)
									hBitmap = GetBitmapFromResources(si.nIdRes);
								else if (!si.strSrcFile.IsEmpty())
									hBitmap = GetBitmapFromFile(si.strSrcFile);
								else if (si.nIdDll)
									hBitmap = GetBitmapFromDll(si.nIdDll, si.strPathDll);
								else if (si.nHandle)
								{
									hBitmap = (HBITMAP)si.nHandle;
									//ENG: If an image handle specified, disables autodelete
									//RUS: ���� ������ ���������� �����������, �� ��������� ��������
									bAutoDelete = FALSE;
								} //if
								
								//ENG: If a handle of an image was retrieved
								//RUS: ���� ���������� ����������� �������
								if (NULL != hBitmap)
								{
									//ENG: Retrieves an original size of an image
									//RUS: �������� ������������ ������ �����������
									m_drawmanager.GetSizeOfBitmap(hBitmap, &sz);

									//ENG: Creates a no specified sizes
									//RUS: ������� ���������� �������
									if (!si.cx && !si.cy)
										si.cx = si.cy = min(sz.cx, sz.cy);
									else if (!si.cx)
										si.cx = si.cy;
									else if (!si.cy)
										si.cy = si.cx;
									
									//ENG: Retrieves an output size
									//RUS: �������� ������� ��� ���������
									if (si.bPercentWidth) si.nWidth = ::MulDiv(si.cx, si.nWidth, 100);
									if (si.bPercentHeight) si.nHeight = ::MulDiv(si.cy, si.nHeight, 100);
									
									//ENG: If a shadow was enabled then set a real size
									//RUS: ���� ���� ��������, �� ������������� �������� ������
									szReal.cx = si.nWidth;
									szReal.cy = si.nHeight;
									if (si.nWidth && si.nHeight && bShadow)
									{
										szReal.cx += m_szOffsetShadow.cx;
										szReal.cy += m_szOffsetShadow.cy;
									} //if
									
									//ENG: Gets a max columns and rows of the images on the bitmap
									//RUS: �������� ������������ ����� ������� � ����� ����������� �� ��������
									nMaxCol = sz.cx / si.cx;
									nMaxRow = sz.cy / si.cy;
									
									if (si.nSpeed)
									{
										if (MODE_FIRSTPASS == m_nNumPass)
										{
											sa.nIndex = si.nIndexImageList;
											sa.nMaxImages = nMaxCol * nMaxRow;
											sa.nSpeed = si.nSpeed;
											sa.nTimerCount = 0;
											m_arrAni.push_back(sa);
										}
										else if (MODE_DRAW == m_nNumPass)
										{
											m_nCurIndexAni ++;
											sa = m_arrAni [m_nCurIndexAni];
											si.nIndexImageList = sa.nIndex;
										} //if
									} //if
									
									//ENG: If a specified index of image is a legitimate value
									//RUS: ���� ��������� ������ ����������� ��������
									if ((si.nIndexImageList < (int)(nMaxCol * nMaxRow)) && nMaxCol && nMaxRow)
									{
										int nMaxSize = nTextWrapWidth - ptOutput.x + m_rcOutput.left;
										if (m_nMaxWidth && ((nMaxSize - szReal.cx) < 0) && nTextWrapWidth) 
										{
											//ENG: Not all text was printed (cause text wrap) 
											//RUS: �� ��� ������ ��� �������� (� ������ �������� ������)
											m_hline.bWrappedLine = TRUE;
											Tag_NewLine(&ptOutput, 1, &szTextArea);
											nBeginLineX = ptOutput.x;
											nSpacesInLine = m_hline.nSpaceChars;
											nRealWidth = m_hline.nWidthLine;
										} //if
										nRealWidth -= szReal.cx;
										
										//ENG: Store a last horizontal alignment
										//RUS: ���������� ��������� �������������� ������������
										if (MODE_FIRSTPASS == m_nNumPass) 
											m_hline.nHorzAlign = m_defStyle.nHorzAlign;
										
										//ENG: Retrieves a vertical coordinates of drawing area
										//RUS: �������� ������������ ���������� ������� ���������
										y = VerticalAlignImage(ptOutput.y, szReal.cy);
										
										//ENG: If an image is exist and not prepare mode
										//RUS: ���� ����������� �������� � �� ���������� ����� ����������
										if (si.nWidth && si.nHeight && (MODE_DRAW == m_nNumPass))
										{
											//ENG: Add an output area to hyperlink list if needed
											//RUS: ���� ���������� ��������� ������� ������ � ������ �����������
											StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + szReal.cx, y + szReal.cy);
											
											//ENG: If a mouse over an image then applies a hot styles
											//RUS: ���� ���� ��� ������������, �� ��������� �������������� �����
											if (m_defStyle.nTypeLink != LINK_NONE)
											{
												if (m_nCurIndexLink == m_nHoverIndexLink)
													si.nStyles = si.nHotStyles;
											} //if
											
											if (!m_bIsEnable)
												si.nStyles = (si.nStyles & 0xFF00) | IMAGE_EFFECT_MONOCHROME;
											
											//ENG: Drawing an image
											//RUS: ��������� �����������
											m_drawmanager.DrawImageList(m_hDC, ptOutput.x, y, si.nWidth, si.nHeight, hBitmap,
												si.nIndexImageList, si.cx, si.cy,
												si.bUseMask, si.crMask, si.nStyles, 
												bShadow, 
												m_szOffsetShadow.cx, m_szOffsetShadow.cy, 
												m_szDepthShadow.cx, m_szDepthShadow.cy, 
												clrShadow);
										} //if
										
										//ENG: Moves to a right of the outputed image
										//RUS: ������������ ������ �� ����������� �����������
										ptOutput.x += szReal.cx;
									} //if
									
									//ENG: If needed delete a handle of an image
									//RUS: ���� ���������� ������� ���������� �����������
									if (bAutoDelete)
										::DeleteObject(hBitmap);
								} //if
							}
							else if (NULL != m_hImageList)
							{
								// Ensure that the common control DLL is loaded. 
								InitCommonControls(); 

								if ((int)si.nIndexImageList < ImageList_GetImageCount(m_hImageList))
								{
									hIcon = ImageList_ExtractIcon(NULL, m_hImageList, si.nIndexImageList);
									if (NULL != hIcon)
									{
										sz.cx = si.nWidth;
										sz.cy = si.nHeight;
										if (si.bPercentWidth) sz.cx = ::MulDiv(m_szImageList.cx, si.nWidth, 100);
										if (si.bPercentHeight) sz.cy = ::MulDiv(m_szImageList.cy, si.nHeight, 100);
										
										szReal.cx = sz.cx;
										szReal.cy = sz.cy;
										if (sz.cx && sz.cy && bShadow)
										{
											szReal.cx += m_szOffsetShadow.cx;
											szReal.cy += m_szOffsetShadow.cy;
										} //if
										
										int nMaxSize = nTextWrapWidth - ptOutput.x + m_rcOutput.left;
										if (m_nMaxWidth && ((nMaxSize - szReal.cx) < 0) && nTextWrapWidth) 
										{
											//ENG: Not all text was printed (cause text wrap) 
											//RUS: �� ��� ������ ��� �������� (� ������ �������� ������)
											m_hline.bWrappedLine = TRUE;
											Tag_NewLine(&ptOutput, 1, &szTextArea);
											nBeginLineX = ptOutput.x;
											nSpacesInLine = m_hline.nSpaceChars;
											nRealWidth = m_hline.nWidthLine;
										} //if
										nRealWidth -= sz.cx;

										if (MODE_FIRSTPASS == m_nNumPass) 
											m_hline.nHorzAlign = m_defStyle.nHorzAlign; //Store a last horizontal alignment
										y = VerticalAlignImage(ptOutput.y, szReal.cy);
										if (sz.cx && sz.cy && (MODE_DRAW == m_nNumPass))
										{
											StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + szReal.cx, y + szReal.cy);
											
											if (m_defStyle.nTypeLink != LINK_NONE)
											{
												if (m_nCurIndexLink == m_nHoverIndexLink)
													si.nStyles = si.nHotStyles;
											} //if
											
											if (!m_bIsEnable)
												si.nStyles = (si.nStyles & 0xFF00) | IMAGE_EFFECT_MONOCHROME;
											
											m_drawmanager.DrawIcon(m_hDC, ptOutput.x, y, 
												sz.cx, sz.cy, hIcon, si.nStyles, 
												bShadow, 
												m_szOffsetShadow.cx, m_szOffsetShadow.cy, 
												m_szDepthShadow.cx, m_szDepthShadow.cy, 
												clrShadow);
											::DestroyIcon(hIcon);
										} //if
										ptOutput.x += szReal.cx;
									} //if
								} //if
							} //if
							break;
						case TAG_STRING:
							//-----------------------------
							//Draws the string
							//-----------------------------
							nIdRes = 0;
							nIdDll = 0;
							sText.Empty();
							while (nIndex < sProperties.GetLength())
							{
								//ENG: Searching a parameters of a tag
								//RUS: ����� ���������� ����
								sValue = GetNextProperty(sProperties, nIndex, sParameter);
								//ENG: If a parameter was found
								//RUS: ���� �������� ������
								if (!sParameter.IsEmpty())
								{
									if (sParameter == _T("idres"))
										nIdRes = GetLengthUnit(sValue, nIdRes);
									else if (sParameter == _T("iddll"))
										nIdRes = GetLengthUnit(sValue, nIdDll);
									else if (sParameter == _T("srcdll"))
										sText = GetStyleString(sValue, sText);
								} //if
							} //while
							if (nIdRes || nIdDll)
							{
								if (nIdRes)
									sText = GetStringFromResource(nIdRes);
								else if (nIdDll)
									sText = GetStringFromDll(nIdDll, sText);
								
								if (!sText.IsEmpty())
								{
									::GetTextExtentPoint32(m_hDC, sText, sText.GetLength(), &sz);
									if (MODE_FIRSTPASS == m_nNumPass) m_hline.nHorzAlign = m_defStyle.nHorzAlign; //Store a last horizontal alignment
									y = VerticalAlignText(ptOutput.y, sz.cy);
									if (MODE_DRAW == m_nNumPass)
									{
										StoreHyperlinkArea(ptOutput.x, y, ptOutput.x + sz.cx, y + sz.cy);
										::TextOut(m_hDC, ptOutput.x, y, sText, sText.GetLength());
									} //if
									ptOutput.x += sz.cx;
								} //if
							} //if
							break;
						} //switch
					} //if
				} //if
		} //if
	} //for
	if (nBeginLineX != ptOutput.x)
	{
		m_hline.bWrappedLine = FALSE;
		Tag_NewLine(&ptOutput, 1, &szTextArea);
	}

	//ENG: Reset an additional interval for space chars
	//RUS: ����� ��������������� ��������� ����� �������
	::SetTextJustification(m_hDC, 0, 0);

	szTextArea.cy = ptOutput.y - lpRect->top;

	//Adds the percent's length to the line's length
	for (i = nFirstLine; i < m_nCurLine; i++)
	{
		m_hline = m_arrHtmlLine [i];
		if (0 != m_hline.nAddPercentWidth)
		{
			m_hline.nWidthLine += ::MulDiv(m_hline.nAddPercentWidth, szTextArea.cx, 100);
			szTextArea.cx = max(szTextArea.cx, m_hline.nWidthLine);
		} //if
	} //for
//
//	if (NULL != lpSize)
//	{
//		szTextArea.cx = m_szOutput.cx;
//		szTextArea.cy = m_szOutput.cy;
//	} //if
	return szTextArea;
} //End DrawHtmlString

void CPPHtmlDrawer::StoreHyperlinkArea(int left, int top, int right, int bottom)
{
	if (m_defStyle.nTypeLink != LINK_NONE)
	{
		STRUCT_HYPERLINK link;
		link.rcArea.left = left;
		link.rcArea.top = top;
		link.rcArea.right = right;
		link.rcArea.bottom = bottom;
		link.sHyperlink = m_defStyle.sHyperlink;
		link.nTypeLink = m_defStyle.nTypeLink;
		link.nIndexLink = m_nCurIndexLink;
		m_arrLinks.push_back(link);
	} //if
} //StoreHyperlinkArea

void CPPHtmlDrawer::SelectNewHtmlStyle(LPCTSTR lpszNameStyle, STRUCT_CHANGESTYLE & cs)
{
	//Unpack a new styles
	UnpackTextStyle(GetTextStyle(lpszNameStyle), cs);
}

BOOL CPPHtmlDrawer::StoreRestoreStyle(BOOL bRestore)
{
	BOOL bOk = FALSE;
	if (bRestore)
	{
		//Restore styles
		if (m_arrStack.size() > 0)
		{
			STRUCT_CHANGESTYLE cs = m_arrStack.back();
			if (cs.strTag == m_defStyle.strTag)
			{
				m_defStyle = cs;
				m_arrStack.pop_back();
				bOk = TRUE;
			} //if
		} //if
		m_defStyle.strTag.Empty();
	}
	else 
	{
		m_arrStack.push_back(m_defStyle);
		bOk = TRUE;
	} //if

	return bOk;
} //End StoreRestoreStyle

void CPPHtmlDrawer::UpdateContext()
{
	::SelectObject(m_hDC, m_hOldFont);
	::DeleteObject(m_hFont);
	m_lfDefault.lfHeight = m_defStyle.nSizeFont;
	m_lfDefault.lfWeight = m_defStyle.nWeightFont;
	m_lfDefault.lfItalic = m_defStyle.bItalicFont;
	m_lfDefault.lfStrikeOut = m_defStyle.bStrikeOutFont;
	m_lfDefault.lfUnderline = m_defStyle.bUnderlineFont;
	_tcscpy (m_lfDefault.lfFaceName, m_defStyle.sFaceFont);
	m_hFont = ::CreateFontIndirect(&m_lfDefault);
	m_hOldFont = (HFONT)::SelectObject(m_hDC, m_hFont);
	::GetTextMetrics(m_hDC, &m_tm);
	
	::SetBkMode(m_hDC, m_defStyle.nBkMode);
	::SetTextColor(m_hDC, m_defStyle.crText);
	::SetBkColor(m_hDC, m_defStyle.crBkgnd);
} //End UpdateContext

int CPPHtmlDrawer::VerticalAlignText(int y, int nHeight)
{
	//Vertical align
	if (MODE_FIRSTPASS == m_nNumPass)
	{
		//If calculate then don't output text
		m_hline.nDescentLine = max(m_hline.nDescentLine, nHeight - m_tm.tmAscent);
		m_hline.nHeightLine = max(m_hline.nHeightLine, m_tm.tmAscent);
	}
	else if (MODE_DRAW == m_nNumPass)
	{
		switch (m_defStyle.nVertAlign)
		{
		case ALIGN_VCENTER:
			y += (m_hline.nHeightLine - m_tm.tmHeight) / 2;
			break;
		case ALIGN_BASELINE:
			y += m_hline.nHeightLine - m_hline.nDescentLine - m_tm.tmAscent;
			break;
		case ALIGN_BOTTOM:
			y += m_hline.nHeightLine - m_tm.tmAscent;
			break;
		} //switch
	} //if
	return y;
} //End VerticalAlignText

int CPPHtmlDrawer::VerticalAlignImage(int y, int nHeight)
{
	//Vertical align
	if (MODE_FIRSTPASS == m_nNumPass)
	{
		//If calculate then don't output text
		m_hline.nHeightLine = max(m_hline.nHeightLine, nHeight);
	}
	else if (MODE_DRAW == m_nNumPass)
	{
		switch (m_defStyle.nVertAlign)
		{
		case ALIGN_VCENTER:
			y += (m_hline.nHeightLine - nHeight) / 2;
			break;
		case ALIGN_BASELINE:
			y += m_hline.nHeightLine - m_hline.nDescentLine - nHeight;
			break;
		case ALIGN_BOTTOM:
			y += m_hline.nHeightLine - nHeight;
			break;
		} //switch
	} //if
	return y;
} //End VerticalAlignImage

void CPPHtmlDrawer::Tag_NewLine(LPPOINT lpPoint, int nNum, LPSIZE lpSize)
{
	//New line
	if (nNum <= 0)
		nNum = 1;

	if (MODE_FIRSTPASS == m_nNumPass)
	{
		if (!m_hline.nHeightLine)
			m_hline.nHeightLine = m_tm.tmHeight;
		lpSize->cx = max(lpSize->cx, lpPoint->x - m_rcOutput.left);
		m_hline.nWidthLine = lpPoint->x - m_rcOutput.left; //Adds the real length of the lines
		m_hline.nHeightLine += m_hline.nDescentLine; //Adds the real height of the lines
		m_arrHtmlLine [m_nCurLine] = m_hline;
	} //if
	
	m_nCurLine ++;

	lpPoint->y += m_hline.nHeightLine * nNum;
	lpPoint->x = InitNewLine(m_rcOutput.left);	
} //End Tag_NewLine

int CPPHtmlDrawer::InitNewLine(int x)
{
	if (MODE_FIRSTPASS == m_nNumPass)
	{
		//ENG: Creates a new line with default parameters
		//RUS: �������� ����� ����� � ����������� ��-���������
		m_hline.nAddPercentWidth = 0;
		m_hline.nDescentLine = 0;
		m_hline.nHeightLine = 0;
		m_hline.nWidthLine = 0;
		m_hline.nHorzAlign = m_defStyle.nHorzAlign;
		m_hline.nSpaceChars = 0;
		m_arrHtmlLine.push_back(m_hline);
	}
	else if (MODE_DRAW == m_nNumPass)
	{
		//ENG: Gets the data of the first line and converts the percent value to the real width
		//RUS: �������� ������ ������ ������ � ����������� ���������� ������ � ��������
		m_hline = m_arrHtmlLine [m_nCurLine];
		int nRealWidth = m_rcOutput.right - m_rcOutput.left;
		
		if (m_hline.nAddPercentWidth)
			m_hline.nWidthLine += ::MulDiv(nRealWidth, m_hline.nAddPercentWidth, 100);

		if ((ALIGN_JUSTIFY == m_hline.nHorzAlign) && m_hline.bWrappedLine)
			::SetTextJustification(m_hDC, nRealWidth - m_hline.nWidthLine, m_hline.nSpaceChars);
		else
			::SetTextJustification(m_hDC, 0, 0);
		
		//ENG: Horizontal coordinate of the begin output
		//RUS: ���������� ������ ������ � ������ ������������
		switch (m_hline.nHorzAlign)
		{
		case ALIGN_CENTER:
			x = m_rcOutput.left + (nRealWidth - m_hline.nWidthLine) / 2;
			break;
		case ALIGN_RIGHT:
			x = m_rcOutput.left + nRealWidth - m_hline.nWidthLine;
			break;
		} //switch
	} //if
	return x;
} //End of InitNewLine

void CPPHtmlDrawer::Tag_Tabulation(LPPOINT lpPoint, int nNum)
{
	//Tabulation
	if (!nNum)
		nNum = 1;
	int nWidth = (lpPoint->x - m_rcOutput.left) % m_nTabSize;
	if (nWidth)
	{
		//aligns with tab
		lpPoint->x += m_nTabSize - nWidth;
		nNum --;
	} //if
	lpPoint->x += (nNum * m_nTabSize);
} //End Tag_Tabulation

/////////////////////////////////////////////////////////////////////////////////////////

void CPPHtmlDrawer::Draw(HDC hDC, LPCTSTR lpszHtml, LPPOINT lpPoint)
{
	//ENG: Preparing an output text
	//RUS: ���������� ������ � ������
	SIZE size;
	PrepareOutput(hDC, lpszHtml, &size);

	//ENG: If output was disabled
	//RUS: ���� ����� ��������
	if (!size.cx || !size.cy)
		return;
	
	//ENG: Calculates an output area
	//RUS: ������� ������� ������
	RECT rect;
	rect.left = lpPoint->x;
	rect.top = lpPoint->y;
	rect.right = rect.left + size.cx;
	rect.bottom = rect.top + size.cy;
	
	//ENG: Output a prepared text
	//RUS: ����� ��������������� ������
	DrawPreparedOutput(hDC, &rect);
} //End Draw

void CPPHtmlDrawer::PrepareOutput(HDC hDC, LPCTSTR lpszHtml, LPSIZE lpSize)
{
	//ENG: Copy initial parameters
	//RUS: ����������� ��������� ����������
	m_hDC = hDC;

	//ENG: Reset text justification
	::SetTextJustification(m_hDC, 0, 0);

	RECT rect;
	rect.left = rect.right = rect.top = rect.bottom = 0;
//	if (m_bIsTextWrapEnabled)
		rect.right = m_nMaxWidth;
	m_csHtmlText = lpszHtml;
	ReplaceSpecChars();
	lpSize->cx = lpSize->cy = 0;
	
	//ENG: If prepared text wasn't empty then return
	//RUS: ���� �������������� ����� �� ������, �� �����
	if (!m_csHtmlText.IsEmpty())
	{
		//ENG: Sets a prepare mode
		//RUS: ������������� ����� ����������
		m_nNumPass = MODE_FIRSTPASS;

		m_arrTables.clear();

		//ENG: Prepares to real output
		//RUS: ���������� � ��������� ������
		DrawHtml(lpSize, &rect);

		if (!lpSize->cx && !lpSize->cy)
			m_csHtmlText.Empty();

		//Cuts a tooltip if his real width more than m_nMaxWidth
		if (m_nMaxWidth/*m_bIsTextWrapEnabled*/ && (lpSize->cx > m_nMaxWidth))
			lpSize->cx = m_nMaxWidth;
		
		lpSize->cx ++;
		lpSize->cy ++;
	} //if
} //End PrepareOutput

////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::DrawPreparedOutput()
//		Draw a string prepared by PrepareOutput method.
//------------------------------------------------------------------
// Parameters:
//		hDC				- Device Context to drawing 
//		lpRect			- Pointer to RECT structure contains a bounding rectangle of
//						  drawing area.
////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::DrawPreparedOutput(HDC hDC, LPCRECT lpRect)
{
	//ENG: If prepared text was empty then return
	//RUS: ���� �������������� ����� ������, �� �����
	if (m_csHtmlText.IsEmpty())
		return;

	//ENG: Copy initial parameters
	//RUS: ����������� ��������� ����������
	m_hDC = hDC;
	SIZE size = {0, 0};

	//ENG: Sets a output mode
	//RUS: ������������� ����� ������
	m_nNumPass = MODE_DRAW;

	RECT rect = *lpRect;
//	if (((rect.right - rect.left) > m_nMaxWidth) && m_bIsTextWrapEnabled)
//		rect.right = rect.left + m_nMaxWidth;

	//ENG: Real output the prepared string
	//RUS: ����� �������������� ������
	DrawHtml(&size, &rect);
} //End of DrawPreparedOutput

// The following appeared in Paul DiLascia's Jan 1998 MSJ articles.
// It loads a "hand" cursor from the winhlp32.exe module
void CPPHtmlDrawer::SetDefaultCursor()
{
	if (m_hLinkCursor == NULL)                // No cursor handle - load our own
    {
#ifdef IDC_HAND
		//This code was added from Zorglab's comments to hyperlink control from Chris Maunder
		m_hLinkCursor = ::LoadCursor(NULL, IDC_HAND); // Load Windows' hand cursor
		if (m_hLinkCursor != NULL)                    // if not available, load it from winhlp32.exe
			return;
#endif //IDC_HAND
		// Get the windows directory
        CPPString strWndDir;
        GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
        strWndDir.ReleaseBuffer();

        strWndDir += _T("\\winhlp32.exe");
        // This retrieves cursor #106 from winhlp32.exe, which is a hand pointer
        HMODULE hModule = LoadLibrary(strWndDir);
        if (hModule) 
		{
            HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
            if (hHandCursor)
                m_hLinkCursor = CopyCursor(hHandCursor);
        } //if
        FreeLibrary(hModule);
    } //if
} //End SetDefaultCursor

void CPPHtmlDrawer::SetHyperlinkCursor(HCURSOR hCursor /* = NULL */)
{
	if ((m_hLinkCursor == hCursor) && (NULL != m_hLinkCursor))
		return;

	if (NULL != m_hLinkCursor)
	{
		::DestroyCursor(m_hLinkCursor);
		m_hLinkCursor = NULL;
	} //if
	

    if (NULL == hCursor)
		SetDefaultCursor();
	else
		m_hLinkCursor = hCursor;
} //End SetHyperlinkCursor

HCURSOR CPPHtmlDrawer::GetHyperlinkCursor() const
{
    return m_hLinkCursor;
} //End GetHyperlinkCursor

/////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::SetCallbackHyperlink
// This function sets or removes the notification messages from the control before display.
//
// Parameters:
//	hWnd [in] -    If non-NULL the control will be send the notification 
//				   to specified window
//				   Else the notification will not send
///////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::SetCallbackHyperlink(HWND hWnd, UINT nMessage, LPARAM lParam /* = 0 */)
{
//	TRACE(_T("CPPHtmlDrawer::SetCallbackHyperlink()\n"));

	m_csCallbackLink.hWnd = hWnd;
	if (NULL == hWnd)
	{
		m_csCallbackLink.nMessage = 0;
		m_csCallbackLink.lParam = 0;
	}
	else
	{
		m_csCallbackLink.nMessage = nMessage;
		m_csCallbackLink.lParam = lParam;
	} //if
} //End SetCallbackHyperlink

void CPPHtmlDrawer::SetCallbackRepaint(HWND hWnd, UINT nMessage, LPARAM lParam /* = 0 */)
{
//	TRACE(_T("CPPHtmlDrawer::SetCallbackRepaint()\n"));

	m_csCallbackRepaint.hWnd = hWnd;
	if (NULL == hWnd)
	{
		m_csCallbackRepaint.nMessage = 0;
		m_csCallbackRepaint.lParam = 0;
	}
	else
	{
		m_csCallbackRepaint.nMessage = nMessage;
		m_csCallbackRepaint.lParam = lParam;
	} //if
} //End SetCallbackRepaint

/////////////////////////////////////////////////////////////////////////////
//  CPPToolTip::SetImageList (public member function)
//    sets the image list to tooltip
//
//  Parameters :
//		nIdBitmap	[in] - Resource IDs of the bitmap to be associated with the image list
//		cx			[in] - Dimensions of each image, in pixels.
//		cy			[in] - Dimensions of each image, in pixels.
//		nCount		[in] - Number of images that the image list initially contains.
//		crMask		[in] - Color used to generate a mask. Each pixel of this color in the 
//						   specified bitmap is changed to black, and the corresponding 
//						   bit in the mask is set to one.
//  Returns :
//		None
//
/////////////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::SetImageList(UINT nIdBitmap, int cx, int cy, int nCount, COLORREF crMask /* = RGB(255, 0, 255) */)
{
	// Load bitmap
	HBITMAP hBitmap = GetBitmapFromResources(nIdBitmap);
	SetImageList(hBitmap, cx, cy, nCount, crMask);
} //End SetImageList

/////////////////////////////////////////////////////////////////////////////
//  CPPToolTip::SetImageList (public member function)
//    sets the image list to tooltip
//
//  Parameters :
//		hBitmap		[in] - Handle of the bitmap to be associated with the image list
//		cx			[in] - Dimensions of each image, in pixels.
//		cy			[in] - Dimensions of each image, in pixels.
//		nCount		[in] - Number of images that the image list initially contains.
//		crMask		[in] - Color used to generate a mask. Each pixel of this color in the 
//						   specified bitmap is changed to black, and the corresponding 
//						   bit in the mask is set to one.
//  Returns :
//		None
//
/////////////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::SetImageList(HBITMAP hBitmap, int cx, int cy, int nCount, COLORREF crMask /* = RGB(255, 0, 255) */)
{
	//ENG: Removes previously image list
	//RUS: ������� ���������� ������ �����������
	if (NULL != m_hImageList)
		::DeleteObject(m_hImageList);

	//ENG: If don't need to create a new image list
	//RUS: ���� �� ����� ��������� ����� ������ �����������
	if (NULL == hBitmap)
		return;

	// Ensure that the common control DLL is loaded. 
	InitCommonControls(); 
	
	m_hImageList = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, nCount, 1);
	ImageList_AddMasked(m_hImageList, hBitmap, crMask);
	m_szImageList.cx = cx;
	m_szImageList.cy = cy;
} //End SetImageList

/////////////////////////////////////////////////////////////////////////////
//  CPPToolTip::GetImageList (public member function)
//    gets the image list from tooltip
//
//  Parameters :
//		sz		   [out] - Dimensions of each image, in pixels.
//  Returns :
//		A pointer to a CImageList object
//
/////////////////////////////////////////////////////////////////////////////
//CImageList * CPPHtmlDrawer::GetImageList(CSize & sz)
//{
//	sz = m_szImageList;
//	return &m_ImageList;
//} //End GetImageList

void CPPHtmlDrawer::EnableEscapeSequences(BOOL bEnable /* = TRUE */)
{
	m_bEnableEscapeSequences = bEnable;
}

void CPPHtmlDrawer::LoadResourceDll(LPCTSTR lpszPathDll, DWORD dwFlags /* = 0 */)
{
	HINSTANCE hInst = NULL;
	if (NULL != lpszPathDll)
		hInst = ::LoadLibraryEx(lpszPathDll, NULL, dwFlags);
	
	SetResourceDll(hInst);

	if (NULL != hInst)
		m_bFreeInstDll = TRUE;
} //End LoadResourceDll

void CPPHtmlDrawer::SetResourceDll(HINSTANCE hInstDll /* = NULL */)
{
	if (NULL != m_hInstDll)
	{
		if (!m_bFreeInstDll)
			return;
		::FreeLibrary(m_hInstDll);
		m_hInstDll = NULL;
	} //if

	m_bFreeInstDll = FALSE;

	if (NULL != hInstDll)
		m_hInstDll = hInstDll;
} //End SetResourceDll


CPPDrawManager * CPPHtmlDrawer::GetDrawManager()
{
	return &m_drawmanager;
} //End GetDrawManager

BOOL CPPHtmlDrawer::IsImageWithShadow(_STRUCT_IMAGE & si)
{
	DWORD dwStyles = si.nStyles | si.nHotStyles;
	if ((dwStyles & IMAGE_EFFECT_MONO_SHADOW) || 
		(dwStyles & IMAGE_EFFECT_GRADIENT_SHADOW))
		return TRUE;
	
	return FALSE;
}

///////////////////////////////////////////////////////////////////////////
// Map of the styles
void CPPHtmlDrawer::SetDefaultCssStyles()
{
	CPPString str = _T("");
	str += _T("body {font-size: 10pt; color:black; font-family:Verdana}\r\n");
	str += _T("p {font-size: 10pt; color:black; font-family:Verdana; font-weight:bold}\r\n");
	str += _T("h1 {font-size: 14pt; color:black; font-family:Verdana; font-weight:bold}\r\n");
	str += _T("h2 {font-size: 13pt; color:#ff9900; font-family:Verdana; font-weight:bold}\r\n");
	str += _T("h3 {font-size: 12pt; color:#ff9900; font-family:Arial; font-weight:bold}\r\n");
	str += _T("h4 {font-size: 10pt; color:black; font-family:Verdana; font-weight:bold}\r\n");
	str += _T("h5 {font-size: 9pt; color:#ff9900; font-family:Verdana; font-weight:bold}\r\n");
	str += _T("h6 {font-size: 65%; color:#626262; font-family:Verdana; font-weight:normal}\r\n");
	str += _T("pre {font-size: 9pt; font-family:\"Courier\"; background-color:#fbedbb}\r\n");
	str += _T("code {color:#990000; font-family:Arial}\r\n");
	str += _T("a:link {text-decoration:none; color:blue}\r\n");
	str += _T("a:hover {text-decoration:underline; color:blue}\r\n");
	str += _T("sub {font-size:65%; vertical-align:bottom}\r\n");
	str += _T("sup {font-size:65%; vertical-align:top}\r\n");
	str += _T("big {font-size:125%}\r\n");
	str += _T("small {font-size:75%}\r\n");
	str += _T(".cpp-comment {color:green; font-style:italic}\r\n");
//	str += _T("td {text-align:center; color:#ff0000; vertical-align:middle}\r\n");
//	str += _T("table {padding:2; border-width:1; color:red}\r\n");

	SetCssStyles(str);
} //End SetDefaultCssStyle

void CPPHtmlDrawer::SetCssStyles(DWORD dwIdCssString, LPCTSTR lpszPathDll /* = NULL */)
{
	CPPString str;
	if (NULL == lpszPathDll)
		str = GetStringFromResource(dwIdCssString);
	else
		str = GetStringFromDll(dwIdCssString, lpszPathDll);
	SetCssStyles(str);
} //End SetCssStyles

void CPPHtmlDrawer::SetCssStyles(LPCTSTR lpszCssString /* = NULL */)
{
	m_mapStyles.clear(); //removes previously styles

	if (NULL == lpszCssString)
	{
		SetDefaultCssStyles();
	}
	else
	{
		CPPString str = (CPPString)lpszCssString;
		m_strCssStyles = str;
		
		CPPString strName;
		CPPString strProperty;
		
		int nBegin;
		TCHAR chSymbol;
		int nIndex = 0;
		
		while (nIndex < str.GetLength())
		{
			//Passes a space in begin string
			if (GetIndexNextAlphaNum(str, nIndex))
			{
				nBegin = nIndex;
				//Searching end of the style name
				chSymbol = GetIndexNextChars(str, nIndex, _T(" {"));
				if ((nIndex > nBegin) && (0 != chSymbol))
				{
					strName = str.Mid(nBegin, nIndex - nBegin);
					if (!strName.IsEmpty())
					{
						if (chSymbol != _T(' '))
							nIndex --;
						chSymbol = GetIndexNextChars(str, nIndex, _T("{"));
						if (0 != chSymbol)
						{
							nBegin = nIndex + 1;
							chSymbol = GetIndexNextChars(str, nIndex, _T("}"));
							if ((nIndex > nBegin) && (0 != chSymbol))
							{
								strProperty = str.Mid(nBegin, nIndex - nBegin);
								SetTextStyle(strName, strProperty);
							} //if
						} //if
					} //if
				} //if
			} //if
		} //while
	} //if
} //End SetCssStyles

LPCTSTR CPPHtmlDrawer::GetCssStyles()
{
	return (LPCTSTR)m_strCssStyles;
} //End GetCssStyles

LPCTSTR CPPHtmlDrawer::GetTextStyle(LPCTSTR lpszStyleName)
{
	CPPString name = (CPPString)lpszStyleName;
	name.MakeLower();
	iter_mapStyles iterMap = m_mapStyles.find(name);
	
	if (iterMap != m_mapStyles.end())
		return (LPCTSTR)iterMap->second;

	//Not found
	return NULL;
} //End GetTextStyle

void CPPHtmlDrawer::SetTextStyle(LPCTSTR lpszStyleName, LPCTSTR lpszStyleValue)
{
	CPPString name = (CPPString)lpszStyleName;
	name.MakeLower();
	iter_mapStyles iterMap = m_mapStyles.find(name);
	
	if (iterMap != m_mapStyles.end())
	{
		//Modifies 
		iterMap->second = (CPPString)lpszStyleValue;
	}
	else
	{
		//Add new
		m_mapStyles.insert(std::make_pair(name, (CPPString)lpszStyleValue));
	} //if
} //End SetTextStyle

void CPPHtmlDrawer::RemoveTextStyle(LPCTSTR lpszStyleName)
{
	CPPString name = (CPPString)lpszStyleName;
	name.MakeLower();
	iter_mapStyles iterMap = m_mapStyles.find(name);
	
	if (iterMap == m_mapStyles.end())
		return; //item was not found
	
	m_mapStyles.erase(iterMap);
} //End RemoveTextStyle

void CPPHtmlDrawer::AddToTextStyle(LPCTSTR lpszStyleName, LPCTSTR lpszAddStyle)
{
} //End AddToTextStyle

void CPPHtmlDrawer::UnpackTextStyle(CPPString strStyle, _STRUCT_CHANGESTYLE & cs)
{
	//Gets a string
	strStyle.MakeLower();
	if (strStyle.IsEmpty())
		return;

	CPPString strName;
	CPPString strParameter;

	int nBegin;
	TCHAR chSymbol;
	int nIndex = 0;
	CPPString str;

	while (nIndex < strStyle.GetLength())
	{
		//Passes a space in begin string
		if (GetIndexNextAlphaNum(strStyle, nIndex))
		{
			nBegin = nIndex;
			//Searching end of the style name
			chSymbol = GetIndexNextChars(strStyle, nIndex, _T(" :"));
			if (0 != chSymbol)
			{
				//Gets a property's name
				strName = strStyle.Mid(nBegin, nIndex - nBegin);

				//Gets a property's value
				strParameter = GetParameterString(strStyle, nIndex, _T(':'));

				//Analyzing name
				if (strName == _T("font-size"))
				{
					cs.nSizeFont = GetLengthUnit(strParameter, cs.nSizeFont, TRUE);
				}
				else if (strName == _T("font-family"))
				{
					if (!strParameter.IsEmpty())
						cs.sFaceFont = strParameter;
				}
				else if (strName == _T("font-style"))
				{
					cs.bItalicFont = GetStyleFontStyle(strParameter, cs.bItalicFont);
				}
				else if (strName == _T("font-weight"))
				{
					cs.nWeightFont = GetStyleFontWeight(strParameter, cs.nWeightFont);
				}
				else if (strName == _T("text-align"))
				{
					cs.nHorzAlign = GetStyleHorzAlign(strParameter, cs.nHorzAlign);
				}
				else if (strName == _T("text-transform"))
				{
					cs.nTextTransform = GetStyleTextTransform(strParameter, cs.nTextTransform);
				}
				else if (strName == _T("color"))
				{
					if (m_bIsEnable)
						cs.crText = GetStyleColor(strParameter, cs.crText);
					else
						cs.crText = GetColorByName("");
				}
				else if (strName == _T("background-color"))
				{
					if (((strParameter == _T("transparent")) && strParameter.IsEmpty()) || !m_bIsEnable)
					{
						cs.nBkMode = TRANSPARENT;
					}
					else
					{
						cs.nBkMode = OPAQUE;
						cs.crBkgnd = GetStyleColor(strParameter, cs.crBkgnd);
					} //if
				}
				else if (strName == _T("text-decoration"))
				{
					StyleTextDecoration(strParameter, cs);
				}
				else if (strName == _T("vertical-align"))
				{
					cs.nVertAlign = GetStyleVertAlign(strParameter, cs.nVertAlign);
				}
				else if (strName == _T("border-color"))
				{
					if (m_bIsEnable)
						cs.crBorderLight = GetStyleColor(strParameter, cs.crBorderLight);
					else
						cs.crBorderLight = GetColorByName("");
					cs.crBorderDark = cs.crBorderLight;
				}
				else if ((strName == _T("border-width")) || (strName == _T("size")))
				{
					cs.nBorderWidth = StyleBorderWidth(strParameter, cs.nBorderWidth);
					if (!cs.nBorderWidth)
						cs.nBorderStyle = CPPDrawManager::PEN_NULL;
					else if (CPPDrawManager::PEN_NULL == cs.nBorderStyle)
						cs.nBorderStyle = CPPDrawManager::PEN_SOLID;
				}
				else if (strName == _T("border-style"))
				{
					cs.nBorderStyle = StyleBorder(strParameter, cs.nBorderStyle);
					if ((CPPDrawManager::PEN_NULL != cs.nBorderStyle) && !cs.nBorderWidth)
						cs.nBorderWidth = 1;
				}
				else if (strName == _T("margin"))
				{
					cs.nMargin = GetLengthUnit(strParameter, cs.nMargin);
				}
				else if (strName == _T("padding"))
				{
					cs.nPadding = GetLengthUnit(strParameter, cs.nPadding);
				} //if
			} //if
		} //if
	} //while
} //End UnpackTextStyle

BOOL CPPHtmlDrawer::GetStyleFontStyle(CPPString & str, BOOL bDefault)
{
	if ((str == _T("normal")) || str.IsEmpty())
	{
		bDefault = FALSE;
	}
	else if ((str == _T("italic")) || (str == _T("oblique"))) 
	{
		bDefault = TRUE;
	} //if

	return bDefault;
} //End GetStyleFontStyle

int CPPHtmlDrawer::GetStyleFontWeight(CPPString & str, int nDefault)
{
	if ((str == _T("normal")) || str.IsEmpty())
	{
		nDefault = FW_NORMAL;
	}
	else if (str == _T("bold"))
	{
		nDefault = FW_BOLD;
	}
	else if (str == _T("bolder"))
	{
		nDefault = 900;
	}
	else if (str == _T("lighter"))
	{
		nDefault = 100;
	}
	else
	{
		nDefault = _ttoi(str);
	} //if

	return nDefault;
} //End GetStyleFontWeight

int CPPHtmlDrawer::GetStyleHorzAlign(CPPString & str, int nDefault)
{
	if ((str == _T("left")) || str.IsEmpty())
	{
		nDefault = ALIGN_LEFT;
	}
	else if (str == _T("center"))
	{
		nDefault = ALIGN_CENTER;
	}
	else if (str == _T("right"))
	{
		nDefault = ALIGN_RIGHT;
	}

	return nDefault;
} //End GetStyleHorzAlign

int CPPHtmlDrawer::GetStyleVertAlign(CPPString & str, int nDefault)
{
	if ((str == _T("baseline")) || str.IsEmpty())
	{
		nDefault = ALIGN_BASELINE;
	}
	else if ((str == _T("middle")) || (str == _T("vcenter")))
	{
		nDefault = ALIGN_VCENTER;
	}
	else if (str == _T("top"))
	{
		nDefault = ALIGN_TOP;
	}
	else if (str == _T("bottom"))
	{
		nDefault = ALIGN_BOTTOM;
	}
	
	return nDefault;
} //End GetStyleVertAlign

int CPPHtmlDrawer::GetStyleTextTransform(CPPString & str, int nDefault)
{
	if ((str == _T("none")) || str.IsEmpty())
	{
		nDefault = TEXT_TRANSFORM_NONE;
	}
	else if (str == _T("uppercase"))
	{
		nDefault = TEXT_TRANSFORM_UPPERCASE;
	}
	else if (str == _T("lowercase"))
	{
		nDefault = TEXT_TRANSFORM_LOWERCASE;
	}
	else if (str == _T("capitalize"))
	{
		nDefault = TEXT_TRANSFORM_CAPITALIZE;
	}
	
	return nDefault;
}

COLORREF CPPHtmlDrawer::GetStyleColor(CPPString & str, COLORREF crDefault)
{
//	if (!m_bIsEnable)
//		return GetColorByName("");
	
	if (!str.IsEmpty())
	{
		if (str.GetAt(0) == _T('#'))
		{
			if (str.GetLength() == 7)
			{
				CPPString strHex = _T("0x");
				strHex += str.Mid(5, 2);
				strHex += str.Mid(3, 2);
				strHex += str.Mid(1, 2);
				crDefault = (COLORREF)_tcstoul(strHex, 0, 0);
			} //if
		}
		else if ((str.GetAt(0) >= '0') && (str.GetAt(0) <= '9'))
			crDefault = (COLORREF)_tcstoul(str, 0, 0);
		else
			crDefault = GetColorByName(str, crDefault);
	} //if 

	return crDefault;
} //End GetStyleColor

int CPPHtmlDrawer::GetLengthUnit(CPPString & str, int nDefault, BOOL bFont /* = FALSE */)
{
	if (str.IsEmpty())
		return nDefault;
	
	if (IsPercentableValue(str))
	{
		//Percent value
		int percent = _ttoi(str.Left(str.GetLength() - 1));
		return ::MulDiv(nDefault, percent, 100);
	} //if

	int nSign = 0;
	if (str.GetAt(0) == _T('+')) nSign = 1;
	else if (str.GetAt(0) == _T('-')) nSign = -1;
	
	if (0 != nSign) str = str.Right(str.GetLength() - 1);
	
	//ENG: This code fragment fixed by Reinhard Steiner(2004/10/20).
	int nValue = _ttoi(str);
	CPPString strUnit;
	if(str.GetLength() >= 2)
		strUnit = str.Right(2);

	if (strUnit == _T("px"))		nDefault = nValue;
	else if (strUnit == _T("ex"))
	{
		SIZE szText;
		CPPString strText = _T("x");
		::GetTextExtentPoint32(m_hDC, strText, strText.GetLength(), &szText);
		nDefault = nValue * szText.cy;
	}
	else if (strUnit == _T("em"))	nDefault = nValue * m_tm.tmHeight;
	else
	{
		//Gets pixel in inch
		nValue *= ::GetDeviceCaps(m_hDC, LOGPIXELSY);
		if (strUnit == _T("in"))		nDefault = nValue;
		else if (strUnit == _T("cm"))	nDefault = (int)((double)nValue / 2.54);
		else if (strUnit == _T("mm"))	nDefault = (int)((double)nValue / 25.4);
		else if (strUnit == _T("pt"))	nDefault = nValue / 72;
		else if (strUnit == _T("pc"))	nDefault = nValue / 6;
		else
		{
			nValue = _tcstoul(str, 0, 0);//_ttoi(str);
			if ((nValue > 0) && (nValue < 8) && bFont)
			{
				int nSize [] = {8, 10, 12, 14, 18, 24, 36};
				nDefault = nSize [nValue - 1];
			}
			else
			{
				nDefault = nValue;
			} //if
		} //if
	} //if
	
	return nDefault;
} //End GetLengthUnit

void CPPHtmlDrawer::StyleTextDecoration(CPPString & str, _STRUCT_CHANGESTYLE & cs)
{
	if (str.IsEmpty())
		str = _T("none");
	
	int nBegin = 0;
	int nEnd = 0;
	CPPString strTemp;
	while (nBegin < str.GetLength())
	{
		if (GetIndexNextAlphaNum(str, nBegin))
		{
			nEnd = nBegin;
			GetIndexNextChars(str, nEnd, _T(" ,"));
			strTemp = str.Mid(nBegin, nEnd - nBegin);
			nBegin = nEnd;
			if (strTemp == _T("none"))
			{
				cs.bUnderlineFont = FALSE;
				cs.bStrikeOutFont = FALSE;
				cs.bOverlineFont = FALSE;
			}
			else if (strTemp == _T("underline"))
			{
				cs.bUnderlineFont = TRUE;
			}
			else if (strTemp == _T("line-through"))
			{
				cs.bStrikeOutFont = TRUE;
			}
			else if (strTemp == _T("overline"))
			{
				cs.bOverlineFont = TRUE;
			}  //if
		} //if
	} //while
} //End StyleTextDecoration

int CPPHtmlDrawer::StyleBorderWidth(CPPString & str, int nDefault)
{
	if (str ==_T("thin"))		nDefault = ::MulDiv(75, nDefault, 100);
	else if (str ==_T("thick"))	nDefault = ::MulDiv(125, nDefault, 100);
	else if (str !=_T("medium"))nDefault = GetLengthUnit(str, nDefault);

	return nDefault;
} //End StyleBorderWidth

int CPPHtmlDrawer::StyleBorder(CPPString & str, int nDefault)
{
	if ((str == _T("none")) || str.IsEmpty()) nDefault = CPPDrawManager::PEN_NULL;
	else if (str == _T("solid")) nDefault = CPPDrawManager::PEN_SOLID;
	else if (str == _T("dotted")) nDefault = CPPDrawManager::PEN_DOT;
	else if (str == _T("dashed")) nDefault = CPPDrawManager::PEN_DASH;
	else if (str == _T("double")) nDefault = CPPDrawManager::PEN_DOUBLE;

	return nDefault;
} //End StyleBorder

void CPPHtmlDrawer::SetDefaultStyles(_STRUCT_CHANGESTYLE & cs)
{
	m_defStyle.strTag.Empty();		//The name of the last opened tag
	
	//Font
	m_defStyle.nSizeFont = 16;		//The height of the logic font
	m_defStyle.nWeightFont = FW_NORMAL;	//The weight of the logic font
	m_defStyle.bItalicFont = FALSE;	//Is italic logic font?
	m_defStyle.bUnderlineFont = FALSE;//Is underline logic font?
	m_defStyle.bStrikeOutFont = FALSE;//Is strikeout logic font?
	m_defStyle.bOverlineFont = FALSE; //Is overline logic font?
	m_defStyle.sFaceFont = _T("Verdana");  //The face name of the logic font
	
	//Color		
	m_defStyle.crText = RGB (0, 0, 0);	//The foreground color 
	m_defStyle.crBkgnd = RGB (255, 255, 255);	//The background color (also begin for the gradient)
	m_defStyle.crBorderLight = RGB (0, 0, 0);	//The border color
	m_defStyle.crBorderDark = RGB (0, 0, 0);	//The border color
	m_defStyle.crMidBkgnd = RGB (255, 255, 255);//The middle background color
	m_defStyle.crEndBkgnd = RGB (255, 255, 255);//The end background color
	
	//Fill
	m_defStyle.nBkMode = TRANSPARENT;		//The background mode for the text (TRANSPARENT, OPAQUE)
	m_defStyle.nFillBkgnd = -1;	//The fill effect of the background
	m_defStyle.strNameResBk.Empty();
	
	//Align
	m_defStyle.nHorzAlign = ALIGN_LEFT;	//The horizontal align
	m_defStyle.nVertAlign = ALIGN_BASELINE;	//The vertical align
	
	//Border
	m_defStyle.nBorderStyle = CPPDrawManager::PEN_NULL;	//The border style
	m_defStyle.nBorderWidth = 0;	//The width of the border
	
	//Text
	m_defStyle.nTextTransform = TEXT_TRANSFORM_NONE;//Transformation of the text (NONE, UPPERCASE, LOWERCASE, CAPITALIZE)
	
	//Margins
	m_defStyle.nMargin = 2;
	
	//Padding
	m_defStyle.nPadding = 0;
	
	//Hyperlink
	m_defStyle.nTypeLink = LINK_NONE;		//The type of the link (NONE, HREF, MESSAGE)
	m_defStyle.sHyperlink.Empty(); //The additional parameter for the link
} //SetDefaultStyles

/////////////////////////////////////////////////////////////////
// Search body of the next tag
//---------------------------------------------------------------
// Parameters:
//     In: str    - a string with html text
//         nIndex - an index of the first char to the searching in the string
//    Out: nIndex - an index of the char in the string after found tag's text
//         strTag - a string contained the tag's text if was found
// Return: A string before found tag's text 
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::SearchNextTag(CPPString & str, CPPString & strTag, int & nIndex)
{
	int nBegin;
	CPPString sText = _T("");
	strTag.Empty();

	while (nIndex < str.GetLength())
	{
		nBegin = nIndex;
		//Searching a chars of the begin tag
		nIndex = str.Find(_T("<"), nIndex);
		if (nIndex < 0)
			nIndex = str.GetLength(); //A tag wasn't found
		sText += str.Mid(nBegin, nIndex - nBegin);
		if (nIndex < str.GetLength())
		{
			//May be it is a begin of the tag?
			if ((nIndex < (str.GetLength() - 1)) && (_T('<') != str.GetAt(nIndex + 1)))
			{
				//Yes of cause!!!
				strTag = GetTagBody(str, nIndex);
				return sText;
			}
			//No, it is a char '<'
			sText += _T("<");
			nIndex += 2;
			break;
		} //if
	} //while
	return sText;
} //End SearchNextTag

/////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::GetTagBody
//	Gets a name of tag with a parameters
//---------------------------------------------------------------
// Parameters:
//	[in]
//		str		-	a string with html text
//		nIndex	-   an index of the begin of the tag. 
//	[out]
//		nIndex  -	an index of char after the tag
//---------------------------------------------------------------
// Return values:
//	A tag's name .
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::GetTagBody(CPPString & str, int & nIndex)
{
	CPPString sTagName = _T("");
	//ENG: Search the tag's end 
	//RUS: ���� ��������� ����
	int nEndOfTag = str.Find(_T('>'), nIndex);
	//ENG: The tag's end was found. Passes a tag's begin char ('<')
	//RUS: ����� ���� ������� ���������� ������ ������ ����
	nIndex++;
	if (nEndOfTag > nIndex)
	{
		//ENG: Gets a full body of tag
		//RUS: �������� ������ ������ ����
		sTagName = str.Mid(nIndex, nEndOfTag - nIndex);
		//ENG: Jump to next char after the tag
		//RUS: ������������ �� ��������� �� ����� ������
		nIndex = nEndOfTag + 1;
	} //if
	return sTagName;
} //End of GetTagBody

/////////////////////////////////////////////////////////////////
// Split a tag to his name and properties
//---------------------------------------------------------------
// Parameters:
//     In: sTag    - a string with tag's text
//    Out: sTag	   - a tag's name
// Return: A property's string 
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::SplitTag(CPPString & sTag)
{
	CPPString sParam(_T(""));
	int nIndex = 0;
	TCHAR tch = GetIndexNextChars(sTag, nIndex, _T(" ="));
	if (tch != _T('\0'))
	{
		//ENG: The separator was found. Splits a tag's body to his name and his parameteres 
		//RUS: ����������� ������. ��������� ���� ���� �� ��� � ���������
		sParam = sTag.Mid(nIndex);
		sTag = sTag.Left(nIndex);
		sParam.TrimLeft(_T(' '));
	} //if
	return sParam;
} //End of SplitTag

CPPString CPPHtmlDrawer::GetNextProperty(CPPString & str, int & nIndex, CPPString & sProp)
{
	CPPString sValue(_T(""));
	sProp.Empty();
	
	//Passes the spaces before a property
	if (GetIndexNextAlphaNum(str, nIndex))
	{
		//The begin of the property was found
		int nBegin = nIndex;
		//Searching end of the property
		GetIndexNextChars(str, nIndex, _T(" ="));
		//Gets a property's string
		sProp = str.Mid(nBegin, nIndex - nBegin);
		TCHAR chFound = GetIndexNextNoChars(str, nIndex, _T(" "));
		if (_T('=') == chFound)
		{
			chFound = GetIndexNextNoChars(str, nIndex, _T(" ="));
			if ((_T('\'') == chFound) || (_T('\"') == chFound))
			{
				nIndex++;
			}
			else
			{
				chFound = _T(' ');
			} //if
			sValue += chFound;
			nBegin = nIndex;
			GetIndexNextChars(str, nIndex, sValue);
			sValue = str.Mid(nBegin, nIndex - nBegin);
			nIndex ++;
		} //if
	} //if
	return sValue;
} //End of GetNextProperty

/////////////////////////////////////////////////////////////////
// Searching the next property of the tag
//---------------------------------------------------------------
// Parameters:
//     In: str    - a string with html text
//         nIndex - an index of the first char to the searching in the string
//    Out: nIndex - an index of the char in the string after found tag's text
// Return: A property's string 
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::SearchPropertyOfTag(CPPString & str, int & nIndex)
{
	CPPString sText = _T("");
	
	//Passes the spaces before a property
	if (GetIndexNextAlphaNum(str, nIndex))
	{
		//The begin of the property was found
		int nBegin = nIndex;
		//Searching end of the property
		TCHAR chFound = GetIndexNextChars(str, nIndex, _T(" ="));
		//Gets a property's string
		sText = str.Mid(nBegin, nIndex - nBegin);
	} //if
	return sText;
} //End SearchPropertyOfTag

/////////////////////////////////////////////////////////////////
// Search a tag
//---------------------------------------------------------------
// Parameters:
//     In: str    - a string with html text
//         nIndex - an index of the first char to the searching in the string
//    Out: nIndex - an index of the first char of the tag
// Return: TRUE if specified tag was found 
//---------------------------------------------------------------
// Example: (strTag = "table") or (strTag = "/table")
/////////////////////////////////////////////////////////////////
BOOL CPPHtmlDrawer::SearchTag(CPPString & str, int & nIndex, CPPString strTag)
{
	strTag = _T("<") + strTag;
	while (nIndex < str.GetLength())
	{
		nIndex = str.Find(strTag, nIndex);
		if (nIndex < 0)
			nIndex = str.GetLength();
		else
		{
			if (nIndex > 0)
			{
				if (str.GetAt(nIndex - 1) != _T('<'))
					return TRUE;
				nIndex += 2;
			}
			else return TRUE;
		} //if
	}
	return FALSE;
} //End SearchTag

/////////////////////////////////////////////////////////////////
// Search a first alpha_num chars or first arithmetic char
//---------------------------------------------------------------
// Parameters:
//     In: str    - a string with html text
//         nIndex - an index of the first char to the searching in the string
//    Out: nIndex - an index of the first found char
// Return: TRUE if specified char was found 
/////////////////////////////////////////////////////////////////
BOOL CPPHtmlDrawer::GetIndexNextAlphaNum(CPPString & str, int & nIndex, BOOL bArithmetic /* = FALSE */)
{
	TCHAR ch;
	for (; nIndex < str.GetLength(); nIndex++)
	{
		ch = str.GetAt(nIndex);
		if ((ch >= _T('0')) && (ch <= _T('9')))
			return TRUE;
		if ((ch >= _T('A')) && (ch <= _T('Z')))
			return TRUE;
		if ((ch >= _T('a')) && (ch <= _T('z')))
			return TRUE;
		if (ch == _T('.'))
			return TRUE;
		if (bArithmetic)
		{
			if ((_T('+') == ch) || (_T('-') == ch) || 
				(_T('*') == ch) || (_T('/') == ch))
				return TRUE;
		} //if
	} //for
	return FALSE;
} //End GetIndexNextAlphaNum

/////////////////////////////////////////////////////////////////
// Search a first char of the chars set
//---------------------------------------------------------------
// Parameters:
//     In: str      - a string with html text
//         nIndex   - an index of the first char to the searching in the string
//		   strChars - the set of the chars
//    Out: nIndex   - an index of the first found char
// Return: A found char or zero if chars was not found  
/////////////////////////////////////////////////////////////////
TCHAR CPPHtmlDrawer::GetIndexNextChars(CPPString & str, int & nIndex, CPPString strChars)
{
	int i;
	for (; nIndex < str.GetLength(); nIndex++)
	{
		for (i = 0; i < strChars.GetLength(); i++)
		{
			if (str.GetAt(nIndex) == strChars.GetAt(i))
				return str.GetAt(nIndex);
		} //for
	} //for
	return 0;
} //End GetIndexNextChars

/////////////////////////////////////////////////////////////////
// Search a first char isn't specified in chars set
//---------------------------------------------------------------
// Parameters:
//     In: str      - a string with html text
//         nIndex   - an index of the first char to the searching in the string
//		   strChars - the set of the chars
//    Out: nIndex   - an index of the first char isn't from chars set
// Return: A found char or zero if all chars was specified in the chars set  
/////////////////////////////////////////////////////////////////
TCHAR CPPHtmlDrawer::GetIndexNextNoChars(CPPString & str, int & nIndex, CPPString strChars)
{
	int i;
	BOOL bFound;
	for (; nIndex < str.GetLength(); nIndex++)
	{
		bFound = FALSE;
		for (i = 0; (i < strChars.GetLength()) && !bFound; i++)
		{
			if (str.GetAt(nIndex) == strChars.GetAt(i))
				bFound = TRUE;
		} //for
		if (!bFound)
			return str.GetAt(nIndex);
	} //for
	return 0;
} //End GetIndexNextNoChars

/////////////////////////////////////////////////////////////////
// Is exist a property's parameter?
//---------------------------------------------------------------
// Parameters:
//     In: str         - a string with html text
//         nIndex      - an index of the first char to the searching in the string
//		   chSeparator - the char is a begin of the parameter
//    Out: nIndex   - an index of the begin parameter (if it exist) or the begin of the next property
// Return: TRUE if parameter was found  
/////////////////////////////////////////////////////////////////
BOOL CPPHtmlDrawer::GetBeginParameter(CPPString & str, int & nIndex, TCHAR chSeparator /* = _T(':') */)
{
	TCHAR ch;
	for (; nIndex < str.GetLength(); nIndex++) 
	{
		//Gets a current char
		ch = str.GetAt(nIndex);
		if (_T(' ') != ch)
		{
			//if it is not space char
			if (chSeparator == ch)
			{
				//if begin of the property's parameter was found
				nIndex ++; //jump to the next char after a begin parameter
				return TRUE;
			}
			else
			{
				return FALSE;
			}//if
		} //if
	} //for
	return FALSE;
} //End GetBeginParameter

/////////////////////////////////////////////////////////////////
// Gets a parameter for the currrent property
//---------------------------------------------------------------
// Parameters:
//     In: str         - a string with html text
//         nIndex      - an index of the first char to the searching in the string
//		   chSeparator - the char is a begin of the parameter
//    Out: nIndex   - an index of the first char after the parameter
// Return: String of the property's parameter (empty if it is not exist)  
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::GetParameterString(CPPString & str, int & nIndex, TCHAR chBeginParam /* = _T(':') */, CPPString strSeparators /* = _T(";") */)
{
	if (GetBeginParameter(str, nIndex, chBeginParam))
	{
		//Parameter for the current property was found
		TCHAR ch = GetIndexNextNoChars(str, nIndex, strSeparators + _T(" "));
		if (0 != ch)
		{
			int nBegin = nIndex;
			if (_T('"') == str.GetAt(nIndex))
			{
				nIndex++;
				TCHAR ch = GetIndexNextChars(str, nIndex, _T("\""));
				if (_T('"') == ch)
				{
					nIndex ++;
					return str.Mid(nBegin + 1, nIndex - nBegin - 2);
				} //if
			}
			else
			{
				GetIndexNextChars(str, nIndex, strSeparators);
				return str.Mid(nBegin, nIndex - nBegin);
			} //if
		} //if
	} //if
	return _T("");
} //End GetParameterString

/////////////////////////////////////////////////////////////////
// Gets a name of the tag
//---------------------------------------------------------------
// Parameters:
//     In: str         - a tag's string
//         nIndex      - an index of the first char to the searching in the string
//    Out: nIndex   - an index of the first char after the parameter
// Return: Name of the tag (empty if it is not exist)  
/////////////////////////////////////////////////////////////////
CPPString CPPHtmlDrawer::GetNameOfTag(CPPString & str, int & nIndex)
{
	CPPString strName = _T("");
	GetIndexNextNoChars(str, nIndex, _T(" "));
	int nBegin = nIndex;
	GetIndexNextChars(str, nIndex, _T(" ="));
	if (nIndex > nBegin)
		strName = str.Mid(nBegin, nIndex - nBegin);
	
	return strName;
} //End GetNameOfTag

/////////////////////////////////////////////////////
// Gets dimensions of the table
//---------------------------------------------------
//  In: sTable - the string contains a HTML table
// Return: cx - number of the columns
//         cy - number of the row
/////////////////////////////////////////////////////
SIZE CPPHtmlDrawer::GetTableDimensions(CPPString & sTable)
{
	//ENG: A table dimensions by default
	//RUS: ������� ������� �� ���������
	SIZE szTable = {0, 0};
	int nIndex = 0;
	int nCol = 0;
	while (nIndex < sTable.GetLength())
	{
		//ENG: Search a begin of the row
		//RUS: ���� ������ ������
		if (SearchTag(sTable, nIndex, _T("tr")))
		{
			//ENG: Increment count of the rows
			//RUS: ����������� ���������� �����
			szTable.cy++;

			//ENG: Count of the columns in current row
			//RUS: ���������� ������� � ������� ������
			nCol = 0;
			int nEndRow;
			int nNewCell;
			do 
			{
				nEndRow = nNewCell = nIndex;
				//ENG: Search an end of the row or a begin of the cell
				//RUS: ���� ����� ������ ��� ������ ������
				SearchTag(sTable, nEndRow, _T("/tr"));
				SearchTag(sTable, nNewCell, _T("td"));
				if (nNewCell < nEndRow)
				{
					nIndex = nNewCell;

					//ENG: Passes a tag body and get a properties of the tag
					//RUS: ���������� ��� ������ ������ � �������� ������ ������� ����
					CPPString sTag;
					SearchNextTag(sTable, sTag, nNewCell);
					CPPString sProperties = SplitTag(sTag);

					//ENG: Analyses a properties of the tag
					//RUS: ����������� �������� ����
					STRUCT_CHANGESTYLE style;
					SIZE szSpan = AnalyseCellParam(sProperties, style, TRUE);

					//ENG: Increment count of the cells
					//RUS: ����������� ���������� ����� � ������
					nCol += szSpan.cx;

					//ENG: Jump to end of the cell
					//RUS: ��������� �� ����� ������
					SearchEndOfCell(sTable, nIndex);
				} //if
			} while (nNewCell < nEndRow);
			nIndex = nEndRow;
			if (nCol > szTable.cx)
				szTable.cx = nCol;
		} //if
	} //while
	return szTable;
} //End GetTableDimensions

/////////////////////////////////////////////////////
// CPPHtmlDrawer::SearchEndOfTable
//	Searching the end of the table
//---------------------------------------------------
//  Parameter:    
//		str - the string contains a HTML table
//		nIndex - index of the first char after the <table> tag
//	Return values:
//		nIndex - index of the begin char of a </table> tag
/////////////////////////////////////////////////////
void CPPHtmlDrawer::SearchEndOfTable(CPPString & str, int & nIndex)
{
	int nBeginTable = nIndex + 7;
	int nEndTable = nIndex + 7;
	int nTable = 1;
	do
	{
		SearchTag(str, nBeginTable, _T("table"));
		SearchTag(str, nEndTable, _T("/table"));
		if (nBeginTable < nEndTable)
		{
			nTable++;
			nBeginTable += 7;
		}
		else if (nEndTable < nBeginTable)
		{
			nTable --;
			nEndTable += 8;
		} //if
	}
	while ((nBeginTable != nEndTable) && nTable); //while
	nIndex = nEndTable - 8;
} //End SearchEndOfTable

/////////////////////////////////////////////////////
// CPPHtmlDrawer::SearchEndOfRow
//	Searching the end of the row
//---------------------------------------------------
//  Parameter:    
//		str - the string contains a HTML table
//		nIndex - index of the first char after the <tr> tag
//	Return values:
//		nIndex - index of the begin char of a </tr> tag
/////////////////////////////////////////////////////
void CPPHtmlDrawer::SearchEndOfRow(CPPString & str, int & nIndex)
{
	nIndex += 4;
	int nBeginRow, nEndRow, nStartTable;
	int nRow = 1;

	do
	{
		nBeginRow = nEndRow = nStartTable = nIndex;

		SearchTag(str, nBeginRow, _T("tr"));
		SearchTag(str, nEndRow, _T("/tr"));
		SearchTag(str, nStartTable, _T("table"));
		
		if ((nStartTable < nBeginRow) && (nStartTable < nEndRow))
		{
			SearchEndOfTable(str, nStartTable);
			nIndex = nStartTable + 6;
		}
		else if (nBeginRow < nEndRow)
		{
			nRow++;
			nIndex = nBeginRow + 4;
		}
		else if (nEndRow < nBeginRow)
		{
			nRow --;
			nIndex = nEndRow + 5;
		} //if
	}
	while ((nIndex < str.GetLength()) && nRow); //while
	nIndex -= 5;
} //End SearchEndOfRow

/////////////////////////////////////////////////////
// CPPHtmlDrawer::SearchEndOfCell
//	Searching the end of the cell
//---------------------------------------------------
//  Parameter:    
//		str - the string contains a HTML table
//		nIndex - index of the first char after the <td> tag
//	Return values:
//		nIndex - index of the begin char of a </td> tag
/////////////////////////////////////////////////////
void CPPHtmlDrawer::SearchEndOfCell(CPPString & str, int & nIndex)
{
	nIndex += 4;
	int nEndCell, nStartTable;
	do
	{
		nEndCell = nStartTable = nIndex;

		SearchTag(str, nEndCell, _T("/td"));
		SearchTag(str, nStartTable, _T("table"));
		
		if (nStartTable < nEndCell)
		{
			SearchEndOfTable(str, nStartTable);
			nEndCell = nIndex = nStartTable + 6;
		}
		else
		{
			nIndex = nEndCell + 5;
		} //if
	}
	while (nStartTable < nEndCell); //while
	nIndex -= 5;
} //End SearchEndOfCell

///////////////////////////////////////////////////////////////////////
// Analysing the cell parameters
//---------------------------------------------------------------------
// Parameters:
//   In: strTag - str string contains parameters of the <table>, <td> or <tr> tags
//           cs - the structures contains the current styles
//		 bTable - 
//  Out:     cs - the structures contains the new styles
///////////////////////////////////////////////////////////////////////
SIZE CPPHtmlDrawer::AnalyseCellParam(CPPString & sProperties, _STRUCT_CHANGESTYLE & cs, BOOL bTable)
{
	SIZE szSpan = {1, 1};
	if (sProperties.IsEmpty())
		return szSpan;
	
	int i = 0;
	CPPString sParameter;
	CPPString sValue;
	
	while (i < sProperties.GetLength())
	{
		//ENG: Searching a parameters of a tag
		//RUS: ����� ���������� ����
		sValue = GetNextProperty(sProperties, i, sParameter);

		//ENG: Processes the specific parameters for <table> tag.
		//RUS: ������������ ������������� ��� ���� <table> ���������
		if(bTable)
		{
			if (sParameter == _T("cellpadding"))
			{
				cs.nMargin = GetLengthUnit(sValue, cs.nMargin);
			}
			else if (sParameter == _T("cellspacing"))
			{
				cs.nPadding = GetLengthUnit(sValue, cs.nPadding);
			} 
			else if (sParameter == _T("background"))
			{
				cs.strNameResBk = sValue;
			} //if
		} //if

		if (sParameter == _T("rowspan"))
		{
			szSpan.cy = GetLengthUnit(sValue, szSpan.cy);
		}
		else if (sParameter == _T("colspan"))
		{
			szSpan.cx = GetLengthUnit(sValue, szSpan.cx);
		}
		else if (sParameter == _T("border"))
		{
			cs.nBorderWidth = GetLengthUnit(sValue, cs.nBorderWidth);
			if (!cs.nBorderWidth)
				cs.nBorderStyle = CPPDrawManager::PEN_NULL;
			else if (CPPDrawManager::PEN_NULL == cs.nBorderStyle)
				cs.nBorderStyle = CPPDrawManager::PEN_SOLID;
		}
		else if (sParameter == _T("borderstyle"))
		{
			cs.nBorderStyle = StyleBorder(sValue, cs.nBorderStyle);
			if ((CPPDrawManager::PEN_NULL != cs.nBorderStyle) && !cs.nBorderWidth)
					cs.nBorderWidth = 1;
		}
		else if (sParameter == _T("bordercolor"))
		{
			if (m_bIsEnable)
				cs.crBorderLight = GetStyleColor(sValue, cs.crBorderLight);
			else
				cs.crBorderLight = GetColorByName("");
			cs.crBorderDark = cs.crBorderLight;
		}
		else if (sParameter == _T("bordercolorlight"))
		{
			if (m_bIsEnable)
				cs.crBorderLight = GetStyleColor(sValue, cs.crBorderLight);
			else
				cs.crBorderLight = GetColorByName("");
		}
		else if (sParameter == _T("bordercolordark"))
		{
			if (m_bIsEnable)
				cs.crBorderDark = GetStyleColor(sValue, cs.crBorderDark);
			else
				cs.crBorderDark = GetColorByName("");
		}
		else if (sParameter == _T("bgcolor"))
		{
			if (m_bIsEnable)
			{
				cs.crBkgnd = GetStyleColor(sValue, cs.crBkgnd);
				if (cs.nFillBkgnd < 0)
					cs.nFillBkgnd = CPPDrawManager::EFFECT_SOLID;
			} //if
		}
		else if (sParameter == _T("bgmidcolor"))
		{
			if (m_bIsEnable)
				cs.crMidBkgnd = GetStyleColor(sValue, cs.crMidBkgnd);
		}
		else if (sParameter == _T("bgendcolor"))
		{
			if (m_bIsEnable)
				cs.crEndBkgnd = GetStyleColor(sValue, cs.crEndBkgnd);
		}
		else if (sParameter == _T("bgeffect"))
		{
			if (m_bIsEnable)
				cs.nFillBkgnd = GetStyleBkgndEffect(sValue, cs.nFillBkgnd);
		}
		else if (sParameter == _T("align"))
		{
			cs.nHorzAlign = GetStyleHorzAlign(sValue, cs.nHorzAlign);
		}
		else if (sParameter == _T("valign"))
		{
			cs.nVertAlign = GetStyleVertAlign(sValue, cs.nVertAlign);
		}
		else if (sParameter == _T("width"))
		{
			cs.nCellWidth = GetLengthUnit(sValue, cs.nCellWidth);
		}
		else if (sParameter == _T("height"))
		{
			cs.nCellHeight = GetLengthUnit(sValue, cs.nCellHeight);
		} //if
	} //for

	//ENG:
	//RUS: 
	if ((CPPDrawManager::PEN_NULL == cs.nBorderStyle) || !cs.nBorderWidth)
	{
		cs.nBorderStyle = CPPDrawManager::PEN_NULL;
		cs.nBorderWidth = 0;
	}
	else if (CPPDrawManager::PEN_SOLID != cs.nBorderStyle)
	{
		cs.nBorderWidth = 1;
	}	//if

	//ENG: 
	//RUS: ��� ����� ������ ������ ����� 1
	if (!bTable && cs.nBorderWidth)
		cs.nBorderWidth = 1;

	return szSpan;
} //End AnalyseCellParam

///////////////////////////////////////////////////////////////////////
// Analysing the image parameters
//---------------------------------------------------------------------
// Parameters:
//   In: sProperties - the sing contains
//           si - the structures contains the image parameters
//  Out:     si - the structures contains the image parameters
///////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::AnalyseImageParam(CPPString & sProperties, _STRUCT_IMAGE & si)
{
	if (sProperties.IsEmpty())
		return;
	
	int i = 0;
	CPPString sParameter;
	CPPString sValue;
	
	while (i < sProperties.GetLength())
	{
		//ENG: Searching a parameters of a tag
		//RUS: ����� ���������� ����
		sValue = GetNextProperty(sProperties, i, sParameter);

//		sParameter = SearchPropertyOfTag(sProperties, i);
//		sValue = GetParameterString(sProperties, i, _T('='), _T(" "));
			
		if (sParameter == _T("index"))
		{
			si.nIndexImageList = GetLengthUnit(sValue, si.nIndexImageList);
		}
		else if (sParameter == _T("idres"))
		{
			si.nIdRes = GetLengthUnit(sValue, si.nIdRes);
		}
		else if (sParameter == _T("iddll"))
		{
			si.nIdDll = GetLengthUnit(sValue, si.nIdDll);
		}
		else if (sParameter == _T("handle"))
		{
			si.nHandle = GetLengthUnit(sValue, si.nHandle);
		}
		else if (sParameter == _T("file"))
		{
			si.strSrcFile = GetStyleString(sValue, si.strSrcFile);
		}
		else if (sParameter == _T("srcdll"))
		{
			si.strPathDll = GetStyleString(sValue, si.strPathDll);
		}
		else if (sParameter == _T("mask"))
		{
			si.crMask = GetStyleColor(sValue, si.crMask);
			si.bUseMask = TRUE;
		}
		else if (sParameter == _T("style"))
		{
			si.nStyles = GetStyleImageShortForm(sValue);
			si.nHotStyles = si.nStyles;
		}
		else if (sParameter == _T("hotstyle"))
		{
			si.nHotStyles = GetStyleImageShortForm(sValue);
		}
		else if (sParameter == _T("cx"))
		{
			si.cx = GetLengthUnit(sValue, si.cx);
		}
		else if (sParameter == _T("cy"))
		{
			si.cy = GetLengthUnit(sValue, si.cy);
		}
		else if (sParameter == _T("width"))
		{
			si.bPercentWidth = IsPercentableValue(sValue);
			si.nWidth = GetLengthUnit(sValue, si.nWidth);
		}
		else if (sParameter == _T("height"))
		{
			si.bPercentHeight = IsPercentableValue(sValue);
			si.nHeight = GetLengthUnit(sValue, si.nHeight);
		}
		else if (sParameter == _T("speed"))
		{
			si.nSpeed = GetLengthUnit(sValue, si.nSpeed);
		} //if
	} //for
} //End AnalyseImageParam

CPPString CPPHtmlDrawer::GetStyleString(CPPString str, CPPString strDefault)
{
	if (!str.IsEmpty())
		strDefault = str;
	return str;
}

///////////////////////////////////////////////////////////////////////
// Analysing the short form of the font style
//---------------------------------------------------------------------
// Parameters:
//   In: str - string contains parameters of the font in the short form
// Short form styles
//       [+] - positive style
//       [-] - inverse style
//       [b] - bold
//       [i] - italic
//       [u] - underlined
//       [s] - strikeout
//       [o] - overline
///////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::GetStyleFontShortForm(CPPString & str)
{
	if (!str.IsEmpty())
	{
		BOOL bSetValue = TRUE;
		for (int i = 0; i < str.GetLength(); i++)
		{
			switch (str.GetAt(i))
			{
			case _T('-'):
				bSetValue = FALSE;
				break;
			case _T('+'):
				bSetValue = TRUE;
				break;
			case _T('b'):
				m_defStyle.nWeightFont = (bSetValue) ? FW_BOLD : FW_NORMAL;
				bSetValue = TRUE;
				break;
			case _T('i'):
				m_defStyle.bItalicFont = bSetValue;
				bSetValue = TRUE;
				break;
			case _T('u'):
				m_defStyle.bUnderlineFont = bSetValue;
				bSetValue = TRUE;
				break;
			case _T('s'):
				m_defStyle.bStrikeOutFont = bSetValue;
				bSetValue = TRUE;
				break;
			case _T('o'):
				m_defStyle.bOverlineFont = bSetValue;
				bSetValue = TRUE;
				break;
			} //switch
		} //for
	} //if
} //End GetStyleFontShortForm

//Get font style value
UINT CPPHtmlDrawer::GetStyleImageShortForm(CPPString & str)
{
	UINT uStyle = 0; //Original image
	
	if (!str.IsEmpty())
	{
		for (int i = 0; i < str.GetLength(); i++)
		{
			switch (str.GetAt(i))
			{
			case _T('d'):
				uStyle |= IMAGE_EFFECT_DARKEN;
				break;
			case _T('g'):
				uStyle |= IMAGE_EFFECT_GRAYEN;
				break;
			case _T('s'):
				if (m_szOffsetShadow.cx || m_szOffsetShadow.cy)
				{
					if (m_bGradientShadow)
						uStyle |= IMAGE_EFFECT_GRADIENT_SHADOW;
					else uStyle |= IMAGE_EFFECT_MONO_SHADOW;
				} //if
				break;
			case _T('l'):
				uStyle |= IMAGE_EFFECT_LIGHTEN;
				break;
			} //switch
		} //for
	} //if
	
	return uStyle;
} //End GetStyleImageShortForm

BOOL CPPHtmlDrawer::IsPercentableValue(CPPString & str)
{
	if (!str.IsEmpty())
	{
		if (str.GetAt(str.GetLength() - 1) == _T('%'))
			return TRUE;
	}
	return FALSE;
}

int CPPHtmlDrawer::GetStyleBkgndEffect(CPPString & str, int nDefault)
{
	if (!str.IsEmpty())
	{
		if (str == _T("transparent"))
			nDefault = -1;
		else if (str == _T("solid"))
			nDefault = CPPDrawManager::EFFECT_SOLID;
		else if (str == _T("hgradient"))
			nDefault = CPPDrawManager::EFFECT_HGRADIENT;
		else if (str == _T("vgradient"))
			nDefault = CPPDrawManager::EFFECT_VGRADIENT;
		else if (str == _T("hcgradient"))
			nDefault = CPPDrawManager::EFFECT_HCGRADIENT;
		else if (str == _T("vcgradient"))
			nDefault = CPPDrawManager::EFFECT_VCGRADIENT;
		else if (str == _T("3hgradient"))
			nDefault = CPPDrawManager::EFFECT_3HGRADIENT;
		else if (str == _T("3vgradient"))
			nDefault = CPPDrawManager::EFFECT_3VGRADIENT;
#ifdef USE_SHADE
		else if (str == _T("noise"))
			nDefault = CPPDrawManager::EFFECT_NOISE;
		else if (str == _T("diagshade"))
			nDefault = CPPDrawManager::EFFECT_DIAGSHADE;
		else if (str == _T("hshade"))
			nDefault = CPPDrawManager::EFFECT_HSHADE;
		else if (str == _T("vshade"))
			nDefault = CPPDrawManager::EFFECT_VSHADE;
		else if (str == _T("hbump"))
			nDefault = CPPDrawManager::EFFECT_HBUMP;
		else if (str == _T("vbump"))
			nDefault = CPPDrawManager::EFFECT_VBUMP;
		else if (str == _T("softbump"))
			nDefault = CPPDrawManager::EFFECT_SOFTBUMP;
		else if (str == _T("hardbump"))
			nDefault = CPPDrawManager::EFFECT_HARDBUMP;
		else if (str == _T("metal"))
			nDefault = CPPDrawManager::EFFECT_METAL;
#endif
		else nDefault = GetLengthUnit(str, nDefault);
	} //if

	return nDefault;
} //End GetStyleBkgndEffect

int CPPHtmlDrawer::GetTableWidth(CPPString & str, int nClientWidth, int nMinWidth, BOOL bSet /* = FALSE */)
{
	if (!str.IsEmpty())
	{
		int i = 0;
		CPPString strProperty;
		CPPString strParameter;
		
		while (i < str.GetLength())
		{
			strProperty = SearchPropertyOfTag(str, i);
			strParameter = GetParameterString(str, i, _T('='), _T(" "));
			strProperty.MakeLower();
			
			if (strProperty == _T("width"))
			{
				if (IsPercentableValue(strParameter))
				{
					int nWidth = GetLengthUnit(strParameter, 100);
					if (bSet)
					{
						if (nWidth <= 100)
							nClientWidth = ::MulDiv(nMinWidth, 100, nWidth);
						else
							nClientWidth = ::MulDiv(nMinWidth, nWidth, 100);
					}
					else
					{
						if (nWidth < 100)
							nClientWidth = ::MulDiv(nClientWidth, nWidth, 100);
					} //if
				}
				else
				{
					nClientWidth = GetLengthUnit(strParameter, nMinWidth);
				} //if
				break;
			} //if
		} //while
	} //if

	if (nClientWidth < nMinWidth)
		nClientWidth = nMinWidth;

	return nClientWidth;
} //End GetTableWidth

void CPPHtmlDrawer::DrawBackgroundImage(HDC hDC, int nDestX, int nDestY, int nWidth, int nHeight, CPPString strNameImage)
{
	if (!m_bIsEnable)
		return;
	if (strNameImage.IsEmpty())
		return;
	if (strNameImage.GetLength() < 6)
		return;

	HBITMAP hBitmap = NULL;

	int nIndex = 0;
	if (GetIndexNextAlphaNum(strNameImage, nIndex))
	{
		int nBegin = nIndex;
		//Searching end of the style name
		TCHAR chSymbol = GetIndexNextChars(strNameImage, nIndex, _T(" :"));
		if (0 != chSymbol)
		{
			//Gets a property's name
			CPPString strName = strNameImage.Mid(nBegin, nIndex - nBegin);
			//Gets a property's value
			CPPString strParameter = GetParameterString(strNameImage, nIndex, _T(':'));
			
			if (strName == _T("idres"))
			{
				UINT nID = (UINT)GetLengthUnit(strParameter, 0);
				hBitmap = GetBitmapFromResources(nID);
			}
			else if (strName == _T("iddll"))
			{
				UINT nID = (UINT)GetLengthUnit(strParameter, 0);
				hBitmap = GetBitmapFromDll(nID);
			}
			else if (strName == _T("file"))
			{
				hBitmap = GetBitmapFromFile(strParameter);
			} //if
		} //if
	} //if

	if (NULL == hBitmap)
		return;

	SIZE sz;
	m_drawmanager.GetSizeOfBitmap(hBitmap, &sz);
	HDC hSrcDC = ::CreateCompatibleDC(hDC);
	HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hSrcDC, hBitmap);
	m_drawmanager.MultipleCopy(hDC, nDestX, nDestY, nWidth, nHeight, hSrcDC, 0, 0, sz.cx, sz.cy);
	::SelectObject(hSrcDC, hOldBitmap);
	::DeleteDC(hSrcDC);

	::DeleteObject(hBitmap);
	hBitmap = NULL;
} //End of DrawBackgroundImage

////////////////////////////////////////////////////////////////////
// CPPHtmlDrawer::SetTooltipShadow()
//		Sets a image's shadow.
//------------------------------------------------------------------
// Parameters:
//		nOffsetX, 
//		nOffsetY		- The offsets of the tooltip's shadow from the tooltip's window.
//		nDarkenPercent	- So far as colors under the shadow will be darken (0 - 100)
//      bGradient		- TRUE to use a gradient shadow.
//		nDepthX,
//		nDepthY			- The gradient depths of the tooltip's shadow.
////////////////////////////////////////////////////////////////////
void CPPHtmlDrawer::SetImageShadow(int nOffsetX, int nOffsetY, BYTE nDarkenPercent /* = 50 */, 
								  BOOL bGradient /* = TRUE */, int nDepthX /* = 7 */, int nDepthY /* = 7 */)
{
	m_szOffsetShadow.cx = nOffsetX;
	m_szOffsetShadow.cy = nOffsetY;
	m_szDepthShadow.cx = nDepthX;
	m_szDepthShadow.cy = nDepthY;
	m_nDarkenShadow = min(100, nDarkenPercent);
	m_bGradientShadow = bGradient;
	BYTE nColor = ::MulDiv(255, 100 - m_nDarkenShadow, 100);
	m_crShadow = RGB(nColor, nColor, nColor);
} //End of SetTooltipShadow

CPPString CPPHtmlDrawer::GetWordWrap(CPPString & str, int nMaxSize, int & nRealSize)
{
	int nCurIndex = 0;
	int nLastIndex = 0;
	SIZE sz = {0, 0};
	TCHAR tch = _T(' ');
	CPPString sResult = _T("");
	while ((sz.cx <= nRealSize) && (0 != tch))
	{
		nLastIndex = nCurIndex;
		nCurIndex ++;
		tch = GetIndexNextChars(str, nCurIndex, PPHTMLDRAWER_BREAK_CHARS);
		::GetTextExtentPoint32(m_hDC, str, nCurIndex, &sz);
	} //while

	if (0 == nLastIndex)
	{
		if (nMaxSize == nRealSize)
		{
			//RUS: �������� � ������ �� ����������, ������� ����� ��������� ������ 
			//     �� ��������, � �� �� ������
			sz.cx = 0;
			for (int i = 1; i < str.GetLength(); i++)
			{
				::GetTextExtentPoint32(m_hDC, str, i + 1, &sz);
				if (sz.cx > nRealSize)
				{
					sResult = str.Left(i);
					str = str.Mid(i);
					::GetTextExtentPoint32(m_hDC, sResult, i, &sz);
					nRealSize = sz.cx;
					return sResult;
				} //if
			} //for
			::GetTextExtentPoint32(m_hDC, str, i, &sz);
			//RUS: ���������� ������� ������, ������� �������
			sResult = str;
			str.Empty();
		}
		else
		{
			//RUS: � ����������� ����� ������� ������ �� ������ �� ������ �����
			sz.cx = 0;
		} //if
	}
	else 
	{
		sResult = str.Left(nLastIndex + 1);
		str = str.Mid(nLastIndex + 1);
		sResult.TrimRight();
		::GetTextExtentPoint32(m_hDC, sResult, sResult.GetLength(), &sz);
//		str.TrimRight();
		str.TrimLeft();
	} //if
	nRealSize = sz.cx;
	return sResult;
} //End of GetWordWrap

int CPPHtmlDrawer::GetCountOfChars(CPPString str, TCHAR tchar /*= _T(' ')*/)
{
	int nCount = 0;
	//ENG:
	//RUS:
	for (int i = 0; i < str.GetLength(); i++)
	{
		if (tchar == str.GetAt(i))
			nCount++;
	} //if
	return nCount;
}

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 Code Project Open License (CPOL)


Written By
Software Developer SvyazInvest
Belarus Belarus
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions