Click here to Skip to main content
15,885,869 members
Articles / Desktop Programming / MFC

Be Sweet - a set of visual source code browsers

Rate me:
Please Sign up or sign in to vote.
4.85/5 (35 votes)
1 Jul 20038 min read 183.7K   4.9K   122  
A set of source code and project browsers to compliment Visual Studio.
// HTStatic.cpp : implementation file
//

#include "stdafx.h"
#include "SMLDoc.h"

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

#define DEFAULT_FONT_SIZE	0


enum {
	TAG_UNKNOWN,

	// Header tag ID's ***MUST*** be in sequential order
	HEADER1,
	HEADER2,
	HEADER3,
	HEADER4,
	HEADER5,
	BEGIN_BODY,
	END_BODY,
	END_HEADER,
	BEGIN_FONT,
	END_FONT,
	BEGIN_CENTER,
	END_CENTER,
	BEGIN_LEFTJUST,
	END_LEFTJUST,
	BEGIN_RIGHTJUST,
	END_RIGHTJUST,
	BEGIN_BOLD,
	END_BOLD,
	BEGIN_UNDERLINE,
	END_UNDERLINE,
	BEGIN_ITALICS,
	END_ITALICS,
	NEWLINE,
};


struct tagInfo {
	char	*tag;
	DWORD	idStart;
	DWORD	idEnd;
};

static tagInfo STagInfo[] = {
	{"body", BEGIN_BODY, END_BODY},
	{"center", BEGIN_CENTER, END_CENTER},
	{"left", BEGIN_LEFTJUST, END_LEFTJUST},
	{"right", BEGIN_RIGHTJUST, END_RIGHTJUST},
	{"font", BEGIN_FONT, END_FONT},
	{"b", BEGIN_BOLD, END_BOLD},
	{"u", BEGIN_UNDERLINE, END_UNDERLINE},
	{"i", BEGIN_ITALICS, END_ITALICS},
	{"br", NEWLINE, NEWLINE},
	{"h1", HEADER1, TAG_UNKNOWN},
	{"h2", HEADER2, TAG_UNKNOWN},
	{"h3", HEADER3, TAG_UNKNOWN},
	{"h4", HEADER4, TAG_UNKNOWN},
	{"h5", HEADER5, TAG_UNKNOWN},
	{"h", TAG_UNKNOWN, END_HEADER},
	{NULL, 0, 0}
};

static DWORD headerFontSizes[5] = {8, 10, 12, 14, 18};



///////////////////////////////////////////////////////////////////////////////
//
//
//	Helper functions specific to parsing MiniHTML
//
//
///////////////////////////////////////////////////////////////////////////////

/* Skip whitespaces and return a pointer to the first non-whitespace character */
static LPCSTR SkipWS(LPCSTR ptr)
{
	while(0 != *ptr && *ptr <= ' ') {
		ptr++;
	}

	return ptr;
}


/* Parses a literal string enclosed in quotes from the markup stream */
static LPCSTR GetString(LPCSTR str, CString &output)
{
	ASSERT('"' == *str);
	LPCSTR end;
	LPCSTR start;
	
	str++;
	start = str;
	end = str;

	/* Find the end of the string literal */
	while(0 != *end && '"' != *end) {
		end++;
	}

	/*
		Make sure the last character found was an ending quote so that we don't
		go past the end of the input stream.
	*/
	str = end;
	if('"' == *str) {
		str++;
	}


	/* Calculate the length and allocate a conversion buffer */
	DWORD length = end - start;
	char *newstr = output.GetBuffer(length + 1);
	LPCSTR src = start;
	char *dst = newstr;

	// Copy over the new string and convert carriage returns to spaces
	while(src < end) {
		if(0x0d == *src || 0x0a == *src) {
			src++;
			if(src < end && 0x0a == *src) {
				src++;
			}

			// Make sure that we only include one space for all carraige returns
			if(dst != newstr && dst[-1] != ' ') {
				*(dst++) = ' ';
			}
			continue;
		}
		*(dst++) = *(src++);
	}
	*dst = 0;

	output.ReleaseBuffer(length);
	
	/* Return a string object containing the literal */
	return str;
}


/*
	Translate an input stream into an ID tag.
	
	Parameters:
		src			Pointer to the input stream.
		tagString	Where the parsed tag or string will be stored.
		isString	Boolean indicating if the parsed data is a string literal
					or a tag identifier.
	Returns:
		Pointer to the first character after the tag or string literal.
*/
static LPCSTR GetID(LPCSTR src, CString &tagString, BOOL &isString)
{
	DWORD length;
	LPCSTR retVal;
	LPCSTR end;

	/* Skip the the beginning of the text to parse */
	src = SkipWS(src);

	/* If the first character is a quote, parse it as a string */
	if('"' == *src) {
		isString = TRUE;
		retVal = GetString(src, tagString);
	} else {
		isString = FALSE;

		end = src;

		// Find the end of the tag string
		while(0 != *end && '>' != *end && 0 != __iscsym(*end)) {
			end++;
		}
		retVal = end;

		// Store the tag ID into the string buffer
		length = end - src;
		char *tmp = tagString.GetBuffer(length + 1);
		memcpy(tmp, src, length);
		tmp[length] = 0;
		tagString.ReleaseBuffer(length);
	}

	return retVal;
}










///////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc::CSMLTagOption
//
//
///////////////////////////////////////////////////////////////////////////////
CSMLDoc::CSMLTagOption::CSMLTagOption(CString &param, CString &value)
{
	m_Param = param;
	m_Value = value;
}










///////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc::CSMLTagOptionList
//
//
///////////////////////////////////////////////////////////////////////////////
CSMLDoc::CSMLTagOptionList::~CSMLTagOptionList()
{
	Reset();
}


void CSMLDoc::CSMLTagOptionList::Reset()
{
	// Delete all options in the list
	while(FALSE == IsEmpty()) {
		delete RemoveHead();
	}
}


CString *CSMLDoc::CSMLTagOptionList::GetParam(LPCSTR param) {
	POSITION pos;

	// Get the head of the list
	pos = GetHeadPosition();

	// Search until we reach the end
	while(NULL != pos) {
		CSMLTagOption *opt = GetNext(pos);

		// If this is the parameter we are looking for, return the param value
		if(0 == opt->m_Param.CompareNoCase(param)) {
			return &opt->m_Value;
		}
	}

	return NULL;
}


CString CSMLDoc::CSMLTagOptionList::GetParamTypeface(CString &defaultFace) {
	CString *value;

	// Get the 'face' param
	value = GetParam(_T("face"));

	if(NULL != value) {
		LPCSTR faceStr = *value;
		UINT id = 0;

		// Translate the typeface
		if(0 == stricmp(faceStr, "ANSI_FIXED_FONT")) {
			id = ANSI_FIXED_FONT;
		} else if(0 == stricmp(faceStr, "ANSI_VAR_FONT")) {
			id = ANSI_VAR_FONT;
		} else if(0 == stricmp(faceStr, "DEFAULT_GUI_FONT")) {
			id = DEFAULT_GUI_FONT;
		} else if(0 == stricmp(faceStr, "OEM_FIXED_FONT")) {
			id = OEM_FIXED_FONT;
		} else if(0 == stricmp(faceStr, "SYSTEM_FONT")) {
			id = SYSTEM_FONT;
		} else if(0 == stricmp(faceStr, "SYSTEM_FIXED_FONT")) {
			id = SYSTEM_FIXED_FONT;
		}

		// If a windows specific font was found, get the typeface
		if(0 != id) {
			LOGFONT logFont;
			::GetObject(::GetStockObject(id), sizeof(LOGFONT), &logFont);

			return logFont.lfFaceName;

		}
		
		return *value;
	}

	return defaultFace;
}


DWORD CSMLDoc::CSMLTagOptionList::GetParamSize(DWORD defaultSize)
{
	CString *value;

	// Find the 'size' parameter
	value = GetParam(_T("size"));
	if(NULL != value) {
		// It was found, so translate it to a number
		defaultSize = atoi(*value);
	}

	return defaultSize;
}


static BOOL GetHexNibble(char ch, UCHAR *out)
{
	BOOL retval;

	retval = TRUE;
	ch = tolower(ch);

	if(ch >= '0' && ch <= '9') {
		*out = ch - '0';
	} else if(ch >= 'a' && ch <= 'f') {
		*out = 10 + (ch - 'a');
	} else {
		retval = FALSE;
	}

	return retval;
}

// Translate a color value or windows system color into an RGB value 
static COLORREF ConvertColor(COLORREF defaultColor, const char *srcTxt)
{
	if(0 == strnicmp(srcTxt, _T("COLOR_"), 6)) {
		srcTxt = &(srcTxt[6]);
		if(0 == stricmp(srcTxt, "3DDKSHADOW")) {
			defaultColor = COLOR_3DDKSHADOW;
		} else if(0 == stricmp(srcTxt, "3DFACE")) {
			defaultColor = COLOR_3DFACE;
		} else if(0 == stricmp(srcTxt, "BTNFACE")) {
			defaultColor = COLOR_BTNFACE;
		} else if(0 == stricmp(srcTxt, "3DHILIGHT")) {
			defaultColor = COLOR_3DHILIGHT;
		} else if(0 == stricmp(srcTxt, "3DLIGHT")) {
			defaultColor = COLOR_3DLIGHT;
		} else if(0 == stricmp(srcTxt, "3DSHADOW")) {
			defaultColor = COLOR_3DSHADOW;
		} else if(0 == stricmp(srcTxt, "ACTIVEBORDER")) {
			defaultColor = COLOR_ACTIVEBORDER;
		} else if(0 == stricmp(srcTxt, "ACTIVECAPTION")) {
			defaultColor = COLOR_ACTIVECAPTION;
		} else if(0 == stricmp(srcTxt, "APPWORKSPACE")) {
			defaultColor = COLOR_APPWORKSPACE;
		} else if(0 == stricmp(srcTxt, "BACKGROUND")) {
			defaultColor = COLOR_BACKGROUND;
		} else if(0 == stricmp(srcTxt, "BTNTEXT")) {
			defaultColor = COLOR_BTNTEXT;
		} else if(0 == stricmp(srcTxt, "CAPTIONTEXT")) {
			defaultColor = COLOR_CAPTIONTEXT;
//		} else if(0 == stricmp(srcTxt, "GRADIENTACTIVECAPTION")) {
//			defaultColor = COLOR_GRADIENTACTIVECAPTION;
//		} else if(0 == stricmp(srcTxt, "GRADIENTINACTIVECAPTION")) {
//			defaultColor = COLOR_GRADIENTINACTIVECAPTION;
		} else if(0 == stricmp(srcTxt, "GRAYTEXT")) {
			defaultColor = COLOR_GRAYTEXT;
		} else if(0 == stricmp(srcTxt, "HIGHLIGHT")) {
			defaultColor = COLOR_HIGHLIGHT;
		} else if(0 == stricmp(srcTxt, "HIGHLIGHTTEXT")) {
			defaultColor = COLOR_HIGHLIGHTTEXT;
//		} else if(0 == stricmp(srcTxt, "HOTLIGHT")) {
//			defaultColor = COLOR_HOTLIGHT;
		} else if(0 == stricmp(srcTxt, "INACTIVEBORDER")) {
			defaultColor = COLOR_INACTIVEBORDER;
		} else if(0 == stricmp(srcTxt, "INACTIVECAPTION")) {
			defaultColor = COLOR_INACTIVECAPTION;
		} else if(0 == stricmp(srcTxt, "INACTIVECAPTIONTEXT")) {
			defaultColor = COLOR_INACTIVECAPTIONTEXT;
		} else if(0 == stricmp(srcTxt, "INFOBK")) {
			defaultColor = COLOR_INFOBK;
		} else if(0 == stricmp(srcTxt, "INFOTEXT")) {
			defaultColor = COLOR_INFOTEXT;
		} else if(0 == stricmp(srcTxt, "MENU")) {
			defaultColor = COLOR_MENU;
		} else if(0 == stricmp(srcTxt, "MENUTEXT")) {
			defaultColor = COLOR_MENUTEXT;
		} else if(0 == stricmp(srcTxt, "SCROLLBAR")) {
			defaultColor = COLOR_SCROLLBAR;
		} else if(0 == stricmp(srcTxt, "WINDOW")) {
			defaultColor = COLOR_WINDOW;
		} else if(0 == stricmp(srcTxt, "WINDOWFRAME")) {
			defaultColor = COLOR_WINDOWFRAME;
		} else if(0 == stricmp(srcTxt, "WINDOWTEXT")) {
			defaultColor = COLOR_WINDOWTEXT;
		} else {
			TRACE1("Unknown system color %s\n", srcTxt);
			return defaultColor;
		}

		defaultColor = GetSysColor(defaultColor);
	} else {
		DWORD newVal;
		int i;
		BOOL err;

		newVal = 0;
		err = FALSE;

		srcTxt = SkipWS(srcTxt);

		// Check for HTML compatible color declaration
		if('#' == *srcTxt) {
			srcTxt++;
		}
		
		// Process 6 hex values
		for(i = 0; i < 6; i++) {
			UCHAR nibble;

			if(FALSE == GetHexNibble(*srcTxt, &nibble)) {
				err = TRUE;
				break;
			}

			newVal <<= 4;
			newVal |= nibble;
			srcTxt++;
		}

		// If no error was found in the conversion, translate it to RGB
		if(FALSE == err) {
			defaultColor = RGB(GetBValue(newVal), GetGValue(newVal), GetRValue(newVal));
		}
	}

	return defaultColor;
}


COLORREF CSMLDoc::CSMLTagOptionList::GetParamColor(COLORREF defaultColor)
{
	CString *value;

	// Get the color argument
	value = GetParam(_T("color"));

	if(NULL != value) {
		// Go translate the color
		defaultColor = ConvertColor(defaultColor, *value);
	}

	return defaultColor;
}


DWORD CSMLDoc::CSMLTagOptionList::GetBkgndColor(COLORREF defaultColor)
{
	CString *value;

	// Get the background color argument
	value = GetParam(_T("bgcolor"));

	if(NULL != value) {
		// Trnaslate the color
		defaultColor = ConvertColor(defaultColor, *value);
	}

	return defaultColor;
}









////////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc::CSMLFontInfo
//
//
////////////////////////////////////////////////////////////////////////////////
CSMLDoc::CSMLFontInfo::CSMLFontInfo()
{
	m_Size = 12;
	m_Style = 0;
	m_Color = RGB(0,0,0);
	m_Face = _T("");
}


CSMLDoc::CSMLFontInfo::CSMLFontInfo(CSMLDoc::CSMLFontInfo *info)
{
	m_Size = info->m_Size;
	m_Style = info->m_Style;
	m_Color = info->m_Color;
	m_Face = info->m_Face;
}


CSMLDoc::CSMLFontInfo::CSMLFontInfo(DWORD size, DWORD style, COLORREF color, CString &face)
{
	m_Size = size;
	m_Style = style;
	m_Color = color;
	m_Face = face;
}


CSMLDoc::CSMLFontInfo::CSMLFontInfo(DWORD size, DWORD style, COLORREF color, LPCSTR face)
{
	m_Size = size;
	m_Style = style;
	m_Color = color;
	if(NULL != face) {
		m_Face = face;
	}
}










///////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc::CSMLTextGroup::CSMLTextBlock
//
//
///////////////////////////////////////////////////////////////////////////////
CSMLDoc::CSMLTextGroup::CSMLTextBlock::CSMLTextBlock(LPCSTR text, CSMLDoc::CSMLFontInfo *info)
{
	ASSERT(NULL != text);
	ASSERT(NULL != info);

	m_Text = text;
	m_Style = info->m_Style;
	m_PointSize = info->m_Size;
	m_Typeface = info->m_Face;
	m_Color = info->m_Color;
}


CString &CSMLDoc::CSMLTextGroup::CSMLTextBlock::GetTypeface()
{
	return m_Typeface;
}


DWORD CSMLDoc::CSMLTextGroup::CSMLTextBlock::GetPointSize()
{
	return m_PointSize;
}


DWORD CSMLDoc::CSMLTextGroup::CSMLTextBlock::GetStyle()
{
	return m_Style;
}


COLORREF CSMLDoc::CSMLTextGroup::CSMLTextBlock::GetColor()
{
	return m_Color;
}


CString &CSMLDoc::CSMLTextGroup::CSMLTextBlock::GetText()
{
	return m_Text;
}










///////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc::CSMLTextGroup
//
//
///////////////////////////////////////////////////////////////////////////////
CSMLDoc::CSMLTextGroup::CSMLTextGroup()
{
}

CSMLDoc::CSMLTextGroup::~CSMLTextGroup()
{
	// Empty the contents of the block array and delete it's elements
	Empty(TRUE);
}

void CSMLDoc::CSMLTextGroup::operator=(CSMLDoc::CSMLTextGroup &line)
{
	// Copy the contents from a text group
	int size = line.m_Blocks.GetSize();
	m_Blocks.SetSize(size);
	for(int i = 0; i < size; i++) {
		m_Blocks[i] = line.m_Blocks[i];
	}
}


int CSMLDoc::CSMLTextGroup::GetBlockCount()
{
	return m_Blocks.GetSize();
}


void CSMLDoc::CSMLTextGroup::Empty(BOOL bDelete)
{
	if(TRUE == bDelete) {
		// Delete all objects in the array
		for(int i = 0; i < m_Blocks.GetSize(); i++) {
			delete m_Blocks[i];
		}
	}

	// Clear the array
	m_Blocks.RemoveAll();
}


CSMLDoc::CSMLTextGroup::CSMLTextBlock *CSMLDoc::CSMLTextGroup::GetBlock(int index)
{
	ASSERT(index >= 0 && index < m_Blocks.GetSize());

	return m_Blocks[index];
}


void CSMLDoc::CSMLTextGroup::Add(CSMLDoc::CSMLTextGroup::CSMLTextBlock *block)
{
	ASSERT(NULL != block);
	m_Blocks.Add(block);
}










///////////////////////////////////////////////////////////////////////////////
//
//
//	CSMLDoc
//
//
///////////////////////////////////////////////////////////////////////////////
CSMLDoc::~CSMLDoc()
{
	PurgeBlocks();
}


CString &CSMLDoc::GetTypeface()
{
	return m_PageTypeface;
}


COLORREF CSMLDoc::GetBkgndColor()
{
	return m_BkgndColor;
}


int CSMLDoc::GetGroupCount()
{
	return m_Blocks.GetSize();
}


void CSMLDoc::PurgeBlocks()
{
	// Delete all objects in the text block
	for(int i = 0; i < m_Blocks.GetSize(); i++) {
		delete m_Blocks[i];
	}
}


CSMLDoc::CSMLTextGroup *CSMLDoc::GetTextGroup(int index)
{
	ASSERT(index >= 0 && index < m_Blocks.GetSize());

	return m_Blocks[index];
}


void CSMLDoc::AddGroup(CSMLDoc::CSMLTextGroup &line)
{
	CSMLTextGroup *newLine = new CSMLTextGroup();
	*newLine = line;
	m_Blocks.Add(newLine);
}


//////////////////////////////////////////////////////////////////////////////
//	Parsing
//////////////////////////////////////////////////////////////////////////////
LPCSTR CSMLDoc::ParseTag(LPCSTR src, CString &tagText, DWORD &tagID, CSMLDoc::CSMLTagOptionList &paramList)
{
	ASSERT(NULL != src);
	ASSERT('<' == *src);

	BOOL isString;
	LPCSTR retVal;
	bool endingTag;

	// Determine if this is a termination tag
	src = SkipWS(src + 1);
	if('/' == *src) {
		src++;
		endingTag = true;
	} else {
		endingTag = false;
	}


	retVal = src;
	tagID = TAG_UNKNOWN;

	/////////////////////////////////////////////////////////////////
	// Skip to the end of the tag statement
	/////////////////////////////////////////////////////////////////
	while(0 != *retVal && '>' != *retVal) {
		if('"' == *retVal) {
			retVal++;
			while(0 != *retVal && '"' != *retVal) {
				retVal++;
			}

			if('"' == *retVal) {
				retVal++;
			}

		} else {
			retVal++;
		}
	}

	if('>' == *retVal) {
		retVal++;
	}


	/////////////////////////////////////////////////////////////////
	// Parse all tag parameters
	/////////////////////////////////////////////////////////////////
	// Get the tag ID
	src = GetID(src, tagText, isString);
	tagInfo *info = STagInfo;

	while(NULL != info->tag) {
		if(0 == tagText.CompareNoCase(info->tag)) {
			tagID = (false == endingTag ? info->idStart : info->idEnd);
			break;
		}
		info++;
	}

	// If a tag ID was found, collect parameters for it
	if(TAG_UNKNOWN != tagID) {
		// Parse the tag parameters
		while(*src != '>') {
			CString param;
			CString value;

			// Skip any whitespaces and unneeded control characters
			src = SkipWS(src);
			while(0 != *src && '>' != *src && !isalpha(*src)) {
				src++;
			}

			// Get the parameter
			src = GetID(src, param, isString);
			src = SkipWS(src);
			// IF there is a parameter value, grap it
			if('=' == *src) {
				src = SkipWS(src + 1);
				src = GetID(src, value, isString);
				src = SkipWS(src);
			}

			paramList.AddTail(new CSMLTagOption(param, value));
		}
	}

	return retVal;
}


BOOL CSMLDoc::ParseSMLText(UINT nID)
{
	BOOL result = FALSE;
	HINSTANCE	hInst;
	HRSRC		hSrc;
	HGLOBAL		hRes;
	CString		text;

	// Get the instance handle of the application
	hInst = AfxGetInstanceHandle();

	// Find the resource
	hSrc = FindResource(hInst, MAKEINTRESOURCE(nID), "SMLDOC");
	if(NULL == hSrc) {
		return FALSE;
	}

	// Load the resource
	hRes = LoadResource(hInst, hSrc);

	if(NULL == hRes) {
		return FALSE;
	}

	// Get a pointer to the resource memory and parse it
	text = (CHAR*)GlobalLock(hRes);
	ParseSMLText(text.GetBuffer(0));
	GlobalUnlock(hRes);

	return TRUE;
}


BOOL CSMLDoc::ParseSMLText(LPCSTR src)
{
	ASSERT(NULL != src);

	CSMLFontInfo		*fontInfo;
	DWORD			length;
	DWORD			tagID;
	LPCSTR			start;
	LPCSTR			end;
	CString			tagString;
	CSMLTextGroup		textGroup;
	CSMLFontInfoList	fontInfoStack;
	CSMLTagOptionList	optionList;
	LOGFONT			logFont;
	bool			inBody;


	// Remove old blocks
	PurgeBlocks();

	// Get the default system font
	::GetObject(::GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &logFont);


	// Reset the default color, style and font information
	inBody = false;
	m_BkgndColor = GetSysColor(COLOR_WINDOW);
	m_PageTypeface = logFont.lfFaceName;
	fontInfo = new CSMLFontInfo(headerFontSizes[DEFAULT_FONT_SIZE],
							   0,
							   GetSysColor(COLOR_WINDOWTEXT),
							   NULL);

	// Parse until we reach a null terminator
	while('\0' != *src) {

		switch(*src) {
		case '<':	// Parse a tag

			// Reset the options list
			optionList.Reset();
			src = ParseTag(src, tagString, tagID, optionList);

			// If we are not inside of the text body, do not process the tag
			if(false == inBody) {
				if(BEGIN_BODY != tagID) {
					continue;
				}
			}

			// Process the tag
			switch(tagID) {
			case TAG_UNKNOWN:
				TRACE1("Unknown tag: \"%s\"\n", tagString.GetBuffer(0));
				break;

			case HEADER1:
			case HEADER2:
			case HEADER3:
			case HEADER4:
			case HEADER5:
				// Save the current font information
				fontInfoStack.AddHead(fontInfo);
				// Create new font information based on the header tag
				fontInfo = new CSMLFontInfo(fontInfo);
				fontInfo->m_Color = optionList.GetParamColor(fontInfo->m_Color);
				fontInfo->m_Size = headerFontSizes[tagID - HEADER1];
				fontInfo->m_Face = optionList.GetParamTypeface(fontInfo->m_Face);
				break;

			case END_FONT:
			case END_HEADER:
				// Make sure we have font information saved
				if(0 != fontInfoStack.GetCount()) {
					// Delete the current font information and get the previous one
					// off of the stack
					delete fontInfo;
					fontInfo = fontInfoStack.RemoveHead();
				}
				break;

			case BEGIN_FONT:
				// Save the current font information
				fontInfoStack.AddHead(fontInfo);
				// Create new font information based on the header tag
				fontInfo = new CSMLFontInfo(fontInfo);
				fontInfo->m_Color = optionList.GetParamColor(fontInfo->m_Color);
				fontInfo->m_Size = optionList.GetParamSize(fontInfo->m_Size);
				fontInfo->m_Face = optionList.GetParamTypeface(fontInfo->m_Face);
				break;

			case END_BODY:
				// End of the text body
				inBody = false;
				break;

			case BEGIN_BODY:
				// Start the text body
				inBody = true;
				// Get any paramters for the text body
				m_BkgndColor = optionList.GetBkgndColor(m_BkgndColor);
				m_PageTypeface = optionList.GetParamTypeface(CString(logFont.lfFaceName));
				break;


			case BEGIN_CENTER:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				fontInfo->m_Style |= JUSTIFY_CENTER;
				break;

			case END_CENTER:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				break;

			case BEGIN_LEFTJUST:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				fontInfo->m_Style |= JUSTIFY_LEFT;
				break;

			case END_LEFTJUST:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				break;

			case BEGIN_RIGHTJUST:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				fontInfo->m_Style |= JUSTIFY_RIGHT;
				break;

			case END_RIGHTJUST:
				// Adjust the font styling
				fontInfo->m_Style &= ~JUSTIFY_MASK;
				break;

			case BEGIN_BOLD:
				// Adjust the font styling
				fontInfo->m_Style |= STYLE_BOLD;
				break;

			case END_BOLD:
				// Adjust the font styling
				fontInfo->m_Style &= ~STYLE_BOLD;
				break;

			case BEGIN_UNDERLINE:
				// Adjust the font styling
				fontInfo->m_Style |= STYLE_UNDERLINE;
				break;

			case END_UNDERLINE:
				// Adjust the font styling
				fontInfo->m_Style &= ~STYLE_UNDERLINE;
				break;

			case BEGIN_ITALICS:
				// Adjust the font styling
				fontInfo->m_Style |= STYLE_ITALICS;
				break;

			case END_ITALICS:
				// Adjust the font styling
				fontInfo->m_Style &= ~STYLE_ITALICS;
				break;

			case NEWLINE:
				// Go to the next line
				AddGroup(textGroup);
				textGroup.Empty(FALSE);
				break;

			default:
				ASSERT(0);
				break;
			}
			continue;

		default:
			// No tag specified, parse it as a text.
			start = src;
			end = src;

			// Parse until end of stream or we reach a tag
			while(0 != *end && '<' != *end) {
				end++;
			}
			length = (end - start);

			// Make sure we are in the text body before adding anything
			if(true == inBody && length > 0) {
				char *txtBuf = new char[length + 1];
				char *dst = txtBuf;

				// Collect and convert the text stream
				while(src < end) {
					if(*src <= ' ') {
						if(dst == txtBuf || (dst > txtBuf && dst[-1] != ' ')) {
							*(dst++) = ' ';
						}
					} else if('%' == *src) {
						UCHAR nibble1;
						UCHAR nibble2;

						// Process hex characters
						src++;

						if(TRUE == GetHexNibble(*src, &nibble1)) {
							src++;
							if(TRUE == GetHexNibble(*src, &nibble2)) {
								nibble1 = (nibble1 << 4) | nibble2;

								if(nibble1 >= 0x20 && nibble1 <= 0x7f) {
									*(dst++) = nibble1;
								}									
							}
						}
					} else {
						*(dst++) = *src;
					}
					src++;
				}

				*dst = 0;
				dst = txtBuf;

				// Remove leading spacing on the FIRST text block in a paragraph/line
				if(0 == textGroup.GetBlockCount()) {
					while(0 != *dst && ' ' == *dst) {
						dst++;
					}
				}

				// If there are actual characters here, add the text block
				if(0 != dst[0]) {
					textGroup.Add(new CSMLTextGroup::CSMLTextBlock(dst, fontInfo));
				}

				delete[] txtBuf;
			}

			src = end;
			break;
		}
	}

	if(0 != textGroup.GetBlockCount()) {
		AddGroup(textGroup);
		textGroup.Empty(FALSE);
	}

	optionList.Reset();

	delete fontInfo;

	return TRUE;
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


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

Comments and Discussions