/* $Header$ */

// base WP htmp parsing implementation

/* *************** Changes ******************
Date			Edit				Bug					Description
12-Jul-24	CR0963			ST/PF/1450	Fix crash with empty dataname variable and data handle.
17-Mar-22	rmm11484								Improved appearance of HTML displayed by docview component.
15-Dec-20	rmm_ncs									New Component Store.
06-Nov-20	rmm10755		ST/DB/1271	Clicking on a link in the code assistant help panel for a command or function now opens the link in the help viewer.
24-Oct-19	rmm_vh									Added support for variable height lines to multi-line edit field: word wrapping in code editor.
19-Dec-17	rmm_emat								Text editor for Omnis language.
14-Oct-16	rmm9111			ST/EC/1374	Problem detecting encoding of HTML document.
04-May-15	rmm8567			ST/HE/1260	Omnis help now opens correctly on Cocoa.
04-Nov-14	rmm8453			ST/EC/1351	Crash on Linux.
21-Oct-14	rmm8448									Memory leaks.
19-Aug-13	rmm64bitux							Linux 64 bit port.
14-Feb-13	rmm64bit5								Changes required for compiling with 64 bit target.
16-Nov-12	rmm_xc45								Changes to allow Xcode 4.5+ to be used for OSX build.
17-Apr-12	rmm64bit1								First set of changes for 64 bit support: 64 bit Omnis data type.
29-Sep-11	pkst6-00009	ST/EC/1270  crash in webcontrol. added safetly and support for ' quoted tags
24-Aug-10	rmm6970			ST/DC/479		IDE help character encoding issue.
25-Jul-07	rmm6181									Changed font for help pages.
20-Jul-07	rmm6171									Improved layout of properties and methods in online help - special Omnis alignment value.
16-Apr-07	rmm6036			ST/RC/1022	Table cells outside table row tags did not draw; various table printing issues fixed.
01-Feb-07	rmm5966			ST/EC/1069	Removed fix AE6740, as it caused this fault, and was actually not required (< is not valid in HTML).
24-Oct-06 AE6740			ST/RC/1011: Docview failed to handle math operators correctly (ie <=)
10-Mar-06 MHMACHUNI               Mach-O unicode changes
02-Sep-04	rmm5079			ST/EC/854		Text outside of paragraphs was erroneously discarded from the body.
01-Jul-04	AE6479			ST/EC/823: 	HTML now supports filepath
26-Feb-04	rmmuni_osx7							Mac OSX Unicode port.
16-Jan-04	rmm4832									Use Monaco as fixed width font on the Mac.
24-Nov-03	rmm4799			ST/HP/046		Added control over point size for fixed width font used by pre and xmp tags.
14-Aug-03	AE6315			ST/PF/263: 	Crash when displaying an html file (+ added colour names)
12-Feb-03	rmmdocgen								Studio 4.0 documentation generator.
04 MAR 02 mpm5070			st/hp/035		Incorrect use of '&' caused crash or missing text
20 NOV 01 mpm5023a								Now make copy of data prior to converting for returning to data field
03 AUG 01 mpm5014			st/ec/657		extraneous </p> inside a <li> </li> was exiting list
03 AUG 01 mpm5013			st/ec/656		If </head> was missing <body> was lost
04-May-01 AE5212									Docview: Minor problem with tags within tags
03 MAY 01 mpm4732									Fixes some html files which contain real '>' chars
23-Apr-01 AE5202									Html didn't cater for jpeg links correctly.
01 AUG 00 mpm4723a								mpm4723 was not correct fix
31 JUL 00 mpm4723			st/dc/176		Fixes blank space problem between paras (zero length text objects were causing problems)
22 MAY 00	mpm4700									Implements printing of document
10-Oct-99 MHn0085                 Fixed Color problem on win16.
********************************************/
// Start jmg_includecasing
#ifndef _extcomp_
#include "extcomp.he"
#endif

#ifndef	_WPHTML_HE_
#include "wphtml.he"
#endif

#ifndef	_WPPARA_HE_
#include "wppara.he"
#endif

#ifndef	_WPTABLE_HE_
#include "wptable.he"
#endif

#ifndef	_WPIMAGE_HE_
#include "wpimage.he"
#endif

#ifndef	_WPXCOMP_HE_
#include "wpxcomp.he"
#endif
 // End jmg_includecasing
 
#include <string.h>
#include <string>					// rmm_emat
#include <unordered_map> 	// rmm_emat

// ###############################################################################
// ################# html character conversion ###################################
// ###############################################################################

#define CHAR_OPEN_TAG		1
#define CHAR_CLOSE_TAG	2

#ifdef ismac

qchar charToAPI( qchar ch );
qchar charToAPI( qchar ch )
{
	// Conversion has already occurred when reading the data, in the Unicode version.
	return ch;
}

#else

//																					0,		1,		2,		3,		4,		5,		6,		7,		8,		9,		a,		b,		c,		d,		e,		f
static qchar htmlToANSI[128] =	/* 80 */ {  128,  129,  130,  131,  132,  133,  134,  135,  133,  137,   95,  139,  140,  141,  142,  143, // 80-8F
																/* 90 */	  144,  145,  146,  147,  148,  149,  151,  150,  152,  153,   95,  155,  156,  157,  158,  159, // 90-9F
																/* a0 */	  160,  161,  162,  163,  164,  165,  166,  167,  168,  169,  170,   96,  172,  173,  174,  175, // A0-AF // rmm3364
																/* b0 */	  176,  177,  178,  179,  180,  181,  182,  183,  184,  185,  186,  187,  188,  189,  190,  191, // B0-BF
																/* c0 */	  192,  193,  194,  195,  196,  197,  198,  199,  200,  201,  202,  203,  204,  205,  206,  207, // C0-CF
																/* d0 */	  208,  209,  210,  211,  212,  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223, // D0-DF // rmm3364
																/* e0 */	  224,  225,  226,  227,  228,  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,  239, // E0-EF
																/* f0 */	  240,  241,  242,  243,  244,  245,  246,  247,  248,  249,  250,  251,  252,  253,  254,  255 }; // F0-FF

qchar charToAPI( qchar ch );
qchar charToAPI( qchar ch )
{
	// Conversion has already occurred when reading the data, in the Unicode version.
	return ch;
}

#endif

// rmmuni: Note - I did not need to change this table for the Unicode port.
// It maps HTML character escape sequences directly to their Unicode value
// All of the data in the sHTMLchars variable is 7 bit Ascii, so we can safely convert it from UTF-8 to qchar
// in order to generate a mapping table to use in the Unicode version.
static const char* sHTMLchars = "\
&AElig; 198&Aacute;193&Acirc; 194&Agrave;192&Atilde;195&Auml;  196&Aring; 197\
&Ccedil;199\
&ETH;   208&Eacute;201&Ecirc; 202&Egrave;200&Euml;  203\
&Iacute;205&Icirc; 206&Igrave;204&Iuml;  207\
&Ntilde;209\
&Oacute;211&Ocirc; 212&Ograve;210&Oslash;216&Otilde;213&Ouml;  214\
&THORN; 222\
&Uacute;218&Ucirc; 219&Ugrave;217&Uuml;  220\
&Yacute;221\
&aacute;225&acirc; 226&acute; 180&aelig; 230&agrave;224&amp;   038&aring; 229&atilde;227&auml;  228\
&brvbar;166\
&ccedil;231&cedil; 184&cent;  162&copy;  169&curren;164\
&deg;   176&divide;247\
&eacute;233&ecirc; 234&egrave;232&eth;   240&euml;  235\
&frac12;189&frac14;188&frac34;190\
&gt;    062\
&iacute;237&icirc; 238&iexcl; 161&igrave;236&iquest;191&iuml;  239\
&laquo; 171&lt;    060\
&macr;  175&micro; 181&middot;183\
&nbsp;  160&not;   172&ntilde;241\
&oacute;243&ocirc; 244&ograve;242&ordf;  170&ordm;  186&oslash;248&otilde;245&ouml;  246\
&para;  182&plusmn;177&pound; 163\
&quot;  034\
&raquo; 187&reg;   174\
&sect;  167&shy;   173&sup1;  185&sup2;  178&sup3;  179&szlig; 223\
&thorn; 254&times; 215\
&uacute;250&ucirc; 251&ugrave;249&uml;   168&uuml;  252\
&yacute;253&yen;   165&yuml;  255\
";

static qlong sHTMLcharsLen = (qlong) strlen(sHTMLchars);	// rmm64bit5
static CHRconvFromBytes *sHtmlToUnicodeMapCfu = 0;

qchar sConvHTMLChar( str15& pName );
qchar sConvHTMLChar( str15& pName )
{
	qlong x = 0; 
	if ( pName[2] == '#' )
	{
		pName.deleet( 1, 2 );
		pName.deleet( pName.length(), 1 );
		stringToQlong( pName, x );
	}
	else
	{
		qlong top = 0;
		qlong bot = sHtmlToUnicodeMapCfu->len() / 11;
		qchar *sHtmlToUnicodeMap = sHtmlToUnicodeMapCfu->dataPtr();
		while ( top <= bot )
		{
			qlong cen = ( top + bot ) / 2;
			int result = MEMmemcmp( &sHtmlToUnicodeMap[cen*11], &pName[1], QBYTELEN(pName[0]) ); // rmm64bitux
			if ( result < 0 )
				top = cen + 1;
			else if ( result > 0 )
				bot = cen - 1;
			else
			{
				pName = str15( 3, MEMincAddr( &sHtmlToUnicodeMap[cen*11], 8 ) );
				stringToQlong( pName, x );
				break;
			}
		}
	}
	return charToAPI( (qchar)x );
}


// ###############################################################################
// ################# html tag conversion #########################################
// ###############################################################################

// rmm6171: added eTagOMNIS for Omnis table alignment
// rmm_emat: added eTagCLASS
// rmm_ncs: added eTagDATAOBJTYPE
enum eTagID {
eTagComment=0,

eTagA,eTagABSBOTTOM,eTagABSMIDDLE,eTagADDRESS,eTagALIGN,eTagALINK,eTagALT,eTagAPIPATH,
eTagAPPLET,eTagAREA,

eTagB,eTagBASE,eTagBASEFONT,eTagBASELINE,eTagBGCOLOR,eTagBGSOUND,eTagBIG,eTagBLOCKQUOTE,
eTagBODY,eTagBORDER,eTagBORDERCOLOR,eTagBORDERCOLORDARK,eTagBORDERCOLORLIGHT,eTagBOTTOM,eTagBR,

eTagCELLPADDING,eTagCELLSPACING,eTagCENTER,eTagCIRCLE,eTagCITE,eTagCLASS,eTagCODE,eTagCOL,eTagCOLGROUP,
eTagCOLOR,eTagCOLSPAN,eTagCOMMENT,eTagCOMPACT,eTagCOMPONENTCTRL,eTagCOMPONENTLIB,

eTagDATAOBJTYPE,eTagDD,eTagDEBUG,eTagDFN,eTagDIR,eTagDISC,eTagDIV,eTagDL,eTagDT,

eTagEM,eTagEMBED,eTagEVENT,

eTagFACE,eTagFONT,eTagFORM,eTagFRAME,eTagFRAMESET,

eTagH1,eTagH2,eTagH3,eTagH4,eTagH5,eTagH6,eTagHEAD,eTagHEIGHT,
eTagHR,eTagHREF,eTagHSPACE,eTagHTML,eTagHTMLPATH,

eTagI,eTagIFRAME,eTagIMG,eTagISINDEX,

eTagKBD,

eTagLEFT,eTagLEFTMARGIN,eTagLI,eTagLINK,eTagLISTING,

eTagMAP,eTagMARQUEE,eTagMENU,eTagMETA,eTagMIDDLE,

eTagNAME,eTagNEXTID,eTagNOBR,eTagNOFRAMES,eTagNOSCRIPT,eTagNOSHADE,eTagNOWRAP,

eTagOBJECT,eTagOL,eTagOMNIS,

eTagP,eTagPLAINTEXT,eTagPRE,

eTagRIGHT,eTagROWSPAN,

eTagS,eTagSAMP,eTagSCRIPT,eTagSCROLLAMOUNT,eTagSCROLLDELAY,eTagSIZE,eTagSMALL,eTagSPAN,
eTagSQUARE,eTagSRC,eTagSTART,eTagSTRIKE,eTagSTRONG,eTagSUB,eTagSUP,

eTagTABLE,eTagTARGET,eTagTBODY,eTagTD,eTagTEXT,eTagTEXTBOTTOM,eTagTEXTMIDDLE,eTagTEXTTOP,
eTagTFOOT,eTagTH,eTagTHEAD,eTagTITLE,eTagTOP,eTagTOPMARGIN,eTagTR,eTagTT,eTagTYPE,

eTagU,eTagUL,

eTagVALIGN,eTagVALUE,eTagVAR,eTagVLINK,eTagVSPACE,

eTagWBR,eTagWIDTH,

eTagX,eTagXCOMP,eTagXMP,

eTagUnknown
};

// IMPORTANT NOTE: Only use spaces to pad the words (DO NOT USE TABS)
const char* sSTRING1 = "\
!--             \
A               \
ABSBOTTOM       \
ABSMIDDLE       \
ADDRESS         \
ALIGN           \
ALINK           \
ALT             \
APIPATH         \
APPLET          \
AREA            \
B               \
BASE            \
BASEFONT        \
BASELINE        \
BGCOLOR         \
BGSOUND         \
BIG             \
BLOCKQUOTE      \
BODY            \
BORDER          \
BORDERCOLOR     \
BORDERCOLORDARK \
BORDERCOLORLIGHT\
BOTTOM          \
BR              \
CELLPADDING     \
CELLSPACING     \
CENTER          \
CIRCLE          \
CITE            \
CLASS           \
CODE            \
COL             \
COLGROUP        \
COLOR           \
COLSPAN         \
COMMENT         \
COMPACT         \
COMPONENTCTRL   \
COMPONENTLIB    \
DATA-OBJTYPE    \
DD              \
DEBUG           \
DFN             \
DIR             \
DISC            \
DIV             \
DL              \
DT              \
EM              \
EMBED           \
EVENT           \
FACE            \
FONT            \
FORM            \
FRAME           \
FRAMESET        \
H1              \
H2              \
H3              \
H4              \
H5              \
H6              \
HEAD            \
HEIGHT          \
HR              \
HREF            \
HSPACE          \
HTML            \
HTMLPATH        \
";

const char* sSTRING2 = "\
I               \
IFRAME          \
IMG             \
ISINDEX         \
KBD             \
LEFT            \
LEFTMARGIN      \
LI              \
LINK            \
LISTING         \
MAP             \
MARQUEE         \
MENU            \
META            \
MIDDLE          \
NAME            \
NEXTID          \
NOBR            \
NOFRAMES        \
NOSCRIPT        \
NOSHADE         \
NOWRAP          \
OBJECT          \
OL              \
OMNIS           \
P               \
PLAINTEXT       \
PRE             \
RIGHT           \
ROWSPAN         \
S               \
SAMP            \
SCRIPT          \
SCROLLAMOUNT    \
SCROLLDELAY     \
SIZE            \
SMALL           \
SPAN            \
SQUARE          \
SRC             \
START           \
STRIKE          \
STRONG          \
SUB             \
SUP             \
TABLE           \
TARGET          \
TBODY           \
TD              \
TEXT            \
TEXTBOTTOM      \
TEXTMIDDLE      \
TEXTTOP         \
TFOOT           \
TH              \
THEAD           \
TITLE           \
TOP             \
TOPMARGIN       \
TR              \
TT              \
TYPE            \
U               \
UL              \
VALIGN          \
VALUE           \
VAR             \
VLINK           \
VSPACE          \
WBR             \
WIDTH           \
X               \
XCOMP           \
XMP             \
";

static char* sHTMLtags = NULL;
static qlong sHTMLtagsLen = 0; // strlen( sHTMLtags );
static qlong sHTMLtagsMaxWordLen = 16;
static CHRconvFromBytes *sHTMLtagCfu = 0;	// rmmuni

eTagID sConvHTMLTag( strxxx& pName );
eTagID sConvHTMLTag( strxxx& pName )
{
	qshort len = pName.length();
	pName.concat(str15(QTEXT("               ")));
	pName[0] = (qchar)len;
	{
		qlong top = 0;
		qlong bot = sHTMLtagCfu->len() / sHTMLtagsMaxWordLen;
		qchar *htmlTags = sHTMLtagCfu->dataPtr();
		while ( top <= bot )
		{
			qlong cen = ( top + bot ) / 2;
			int result = MEMmemcmp( &htmlTags[cen*sHTMLtagsMaxWordLen], &pName[1], QBYTELEN(sHTMLtagsMaxWordLen) ); // rmm64bitux
			if ( result < 0 )
				top = cen + 1;
			else if ( result > 0 )
				bot = cen - 1;
			else
				return (eTagID) cen;
		}
	}
	return (eTagID)eTagUnknown;
}

// Start rmmdocgen
// rmm11484: Updated fonts
#if (defined(iswin32) && !defined(isunix))
	const str15 propCodeFontName(QTEXT("Consolas"));
#elif defined(isunix)
	const str15 propCodeFontName(QTEXT("Courier New")); // rmm6181
#else
	#ifdef ismac
		const str15 propCodeFontName(QTEXT("Menlo"));	// rmm4832
	#else
		const str15 propCodeFontName(QTEXT("Courier"));
	#endif
#endif
// End rmmdocgen

static	eTagID	sTagID;									// current tag ID - set by nextHTMLtag
static	str31		sTag;										// current tag name (upper cased) - set by nextHTMLtag
static	eTagID	sPropTagID;							// current property tag ID - set by nextHTMLtagProperty
static	str31		sPropTag;								// current property name (upper cased) - set by nextHTMLtagProperty
static	str31		sPropTagX;							// current property name (in original case) - set by nextHTMLtagProperty
static	str255	sPropValue;							// current property value (upper cased) - set by nextHTMLtagProperty
static	str255	sPropValueX;						// current property value (in original case) - set by nextHTMLtagProperty
static	qbool		sIsEndTag;							// if true its a end tag i.e </table> - set by nextHTMLtag and nextHTMLtagProperty
static	qbool		sTagIsClosed;						// if true theend of the tag '>' has been reached (no more tag properties) - set by nextHTMLtag and nextHTMLtagProperty
static	qbool		sTagHaveBreak=qfalse;		// if true we have parsed a break tag and the next text must start on a new line - set by parseHTMLmain


// ###############################################################################
// ################# WPHtmlClass public ##########################################
// ###############################################################################


WPHtmlClass::WPHtmlClass( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting, qshort pPointSizeForPreAndXmp, qbool pCodeAssistant) // mpm4700 // rmm4799 // rmm_emat
						:WPDocClass( pCrlHwnd, pEventHwnd, pIdent, pPrinting, pCodeAssistant) // rmm_emat
{
	mPointSizeForPreAndXmp = pPointSizeForPreAndXmp; // rmm4799
	mSpareTextPara = 0;	// rmm5079
}


WPHtmlClass::~WPHtmlClass()
{
}


WPHtmlClass* WPHtmlClass::make( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting, // mpm4700
																WPDocInfoStruct* pDocInfo, str255& pDocument, qshort pPointSizeForPreAndXmp, qbool pCodeAssistant, qchar* pSrchWords) // rmm4799 // rmm_emat
{
	WPHtmlClass* obj = new WPHtmlClass(pCrlHwnd, pEventHwnd, pIdent, pPrinting, pPointSizeForPreAndXmp, pCodeAssistant); // mpm4700 // rmm4799 // rmm_emat
	if ( obj )
	{
		if ( pDocInfo ) 
			obj->mDocInfo = *pDocInfo;
		else
			obj->mDocInfo.init( eWPdocHTML );
	}
	// Start rmm_vh
	if (obj->mCodeAssistant)
		obj->mDocInfo.mBackColor = GDI_COLOR_OMNIS_CODE_ASSISTANT_POPUP;
	// End rmm_vh
	obj->parseDocument( pDocument, pSrchWords );
	return obj;
}

// AE6479
WPHtmlClass* WPHtmlClass::make( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting, // mpm4700
 															WPDocInfoStruct* pDocInfo,EXTfldval *fldval, qshort pPointSizeForPreAndXmp, qbool pCodeAssistant, qchar* pSrchWords) // rmm_emat
{
	WPHtmlClass* obj = new WPHtmlClass( pCrlHwnd, pEventHwnd, pIdent, pPrinting,pPointSizeForPreAndXmp,pCodeAssistant); // mpm4700 // rmm_emat
	if ( obj )
	{
		if ( pDocInfo ) 
			obj->mDocInfo = *pDocInfo;
		else
			obj->mDocInfo.init( eWPdocHTML );
	}
	obj->parseText( fldval,pSrchWords);
	return obj;
}


// ###############################################################################
// ################# html parsing ################################################
// ###############################################################################


WPObjectClass* WPHtmlClass::saveParent( WPObjectClass* pParent )
{
	if ( pParent == this )
	{
		return WPParagraphClass::make( this, this, &mParagraphInfo );
	}
	else return pParent;
}


qbool WPHtmlClass::exitPara( qlong& pInd )
{
	if ( mInPara )
	{
		pInd--;
		while ( pInd && mDocPtr[pInd] != CHAR_OPEN_TAG ) pInd--;
		mInPara = qfalse; mExitPara = qtrue;
		return qtrue;
	}
	return qfalse;
}

#ifdef ismac
  #ifdef ismach_o // MHMACHUNI
  	static str80 brkch(CFSTR(" .,;:!?)]}([{+-*/|&><=")); //characters used for word breaking // rmm_xc45: Removed unusual Mac characters - these have already been removed for the core and other components
  #else
  	static str80 brkch(QTEXT(" .,;:!?)]}([{+-*/|&><=�Ȳ�")); //characters used for word breaking
	#endif
#else
	static str80 brkch(QTEXT(" .,;:!?)]}([{+-*/|&><=\xab\xbb\x84\x85"));
#endif

static qlong	sSrchWrdCnt;
static qchar* sSrchWrd[5];
static qlong	sSrchWrdLen[5];

qbool findSrchWord( qchar* pDocWrd );
qbool findSrchWord( qchar* pDocWrd )
{
	for ( qlong cur = 0 ; cur < sSrchWrdCnt ; cur++ )
	{
		if ( uprCmp( sSrchWrd[cur], pDocWrd, sSrchWrdLen[cur] ) == 0 )
		{
			qchar ch = pDocWrd[sSrchWrdLen[cur]];
			if ( ch <= ' ' || brkch.pos( ch ) ) return qtrue;
		}
	}
	return qfalse;
}



void WPHtmlClass::convHTMLsrch( qchar* pSrchWords )
{
	static qchar tagSrchStart[4]	= {1,'x',2,0};
	static qchar tagSrchEnd[5]		= {1,'/','x',2,0};
	
	// initialize search words
	sSrchWrdCnt = 0;
	while ( sSrchWrdCnt < 5 )
	{
		while ( *pSrchWords == ' ' ) pSrchWords++;
		if ( *pSrchWords == 0 ) break;
		sSrchWrd[sSrchWrdCnt] = pSrchWords;
		while ( *pSrchWords != ' ' && *pSrchWords != '\0' ) pSrchWords++;
		sSrchWrdLen[sSrchWrdCnt] = (qlong) (pSrchWords - sSrchWrd[sSrchWrdCnt]);	// rmm64bit5
		sSrchWrdCnt++;
	}

	qlong newSize = mDocPtr.charLen();
	qHandle newHan = HANglobalAlloc( QBYTELEN(newSize) );
	qHandleTextPtr newPtr( newHan, 0 );
	qlong newPos = 0; qlong docPos = 0; 
	qbool inSrchWord = qfalse, inWord = qfalse, inTag = qfalse;
	
	// advance to body tag
	while ( nextHTMLtag( docPos ) )
	{
		if ( sTagID == eTagBODY )
		{
			ignoreHTMLtagSimple( docPos );
			MEMmovel( &mDocPtr[0], &newPtr[0], QBYTELEN(docPos) );
			newPos = docPos;
			break;
		}
	}
	// slowly move data to new handle 
	while ( docPos < (qlong)mDocPtr.charLen() )
	{
		qchar ch = mDocPtr[docPos];
		if ( inTag )
		{
			if ( ch == CHAR_CLOSE_TAG ) 
			{
				inTag = qfalse;
				inWord = qfalse; // rmmdocgen
			}
		}
		else if ( ch == CHAR_OPEN_TAG )
		{
			if ( inSrchWord )
			{
				MEMmovel( tagSrchEnd, &newPtr[newPos], QBYTELEN(4) );
				newPos+=4;
				inSrchWord = qfalse;
			}
			inTag = qtrue;
			inWord = qfalse; // rmmdocgen
		}
		else if ( ch <= ' ' || brkch.pos(ch) )
		{
			if ( inSrchWord )
			{
				MEMmovel( tagSrchEnd, &newPtr[newPos], QBYTELEN(4) );
				newPos+=4;
				inSrchWord = qfalse;
			}
			inWord = qfalse;
		}
		else
		{ // beginning of a word
			if ( !inWord )
			{
				inWord = qtrue;
				inSrchWord = findSrchWord( &mDocPtr[docPos] );
				if ( inSrchWord )
				{
					MEMmovel( tagSrchStart, &newPtr[newPos], QBYTELEN(3) );
					newPos+=3;
				}
			}
		}
		
		newPtr[newPos] = ch;
		docPos++; newPos++;
		
		if ( newPos + 10 > newSize )
		{
			if ( HANglobalReAlloc( newHan, QBYTELEN(newSize + 100) ) )
			{
				newSize += 100;
				newPtr = qHandleTextPtr( newHan, 0 );
			}
			else break;
		}
	}
	if ( mOrigDocHan ) HANglobalFree( mOrigDocHan ); // mpm5023b
	mOrigDocHan = mDocHan; // mpm5023a
	newPtr.dataLen( QBYTELEN(newPos) );
	mDocHan = newHan;
	mDocPtr = newPtr;
}


void WPHtmlClass::convHTML()
{
	qlong newSize = mDocPtr.charLen();
	qHandle newHan = HANglobalAlloc( QBYTELEN(newSize) );
	qHandleTextPtr newPtr( newHan, 0 );
	qlong newPos = 0; qlong docPos = 0;
	qbool rawText = qfalse;
	qshort openTag = 0; // mpm4732 AE5212
	while ( docPos < (qlong)mDocPtr.charLen() )
	{
		qchar ch = mDocPtr[docPos];
		if ( ch >= 0x80 ) ch = charToAPI( ch );
		switch ( ch )
		{
			case '&':
			{ // mpm5070 begins
				qchar startch = ch; qlong start = docPos; qbool ok = qfalse;
				while ( docPos < (qlong)mDocPtr.charLen() && (docPos-start)<8 )
				{
					if ( ch==';' ) { ok=qtrue; break; }
					docPos++; 
					ch = mDocPtr[docPos];
				}
				if ( ok )
				{
					str15 name( (qshort) (docPos - start + 1), &mDocPtr[start] );
					ch = sConvHTMLChar( name );
					if ( ch != 9 )
					{
						newPtr[newPos] = ch;
						newPos++; docPos++;
						break;
					}
					// let it run into next case
				}
				else
				{
					docPos = start;
					newPtr[newPos] = startch;
					newPos++; docPos++;
					break;
				}
			} // mpm5070 ends
			case 9:
			{
				static qchar sTabSpaces[4] = {' ',' ',' ',' '};
				MEMmovel( sTabSpaces, &newPtr[newPos], QBYTELEN(4) );
				newPos+=4; docPos++;
				break;
			}
			case 13:
			case 10:
			{
				newPtr[newPos] = rawText ? 13 : ' '; // keep CR character if in raw text tags
				newPos++; docPos++;
				if ( rawText )
				{
					if ( docPos < (qlong)mDocPtr.charLen() && mDocPtr[docPos] == 10 ) docPos++;
				}
				else
				{
					while ( docPos < (qlong)mDocPtr.charLen() && mDocPtr[docPos]<=' ' ) docPos++;
				}
				break;
			}
			case '<':
			case '>':
			{ // look ahead to see what tag we will be in
				if ( ch == '>' ) // mpm4732 begins
				{
					if (openTag) // AE5212
					{
						openTag--;
						if ( openTag==0 )
							ch = CHAR_CLOSE_TAG;
						else
							ch = ' ';	
					}
				} // mpm4732 ends
				else
				{
					if (openTag)	// AE5212
						ch = ' ';
					else
					{	
						qlong pos = docPos + 1; sPropTag = qnil; sIsEndTag = qfalse;
						if ( pos < (qlong)mDocPtr.charLen() )
						{
							if ( mDocPtr[pos] == '/' )
							{
								sIsEndTag = qtrue; pos++;
							}
							while ( pos < (qlong)mDocPtr.charLen() )
							{
								ch = mDocPtr[pos];
								if ( ch == '>' || ch == ' ' )
									break;
								else
									sPropTag.concat( ch );
								pos++;
							}
						}
						sPropTag.upps();
						sTagID = sConvHTMLTag( sPropTag );
						if ( sTagID == eTagPRE || sTagID == eTagXMP || sTagID == eTagSCRIPT )
							rawText = !sIsEndTag;
						ch = CHAR_OPEN_TAG;
					}
					openTag++; // mpm4732
				}
				// let run into next case
			}
			default:
			{
				newPtr[newPos] = ch;
				newPos++; docPos++;
				break;
			}
		}
		if ( newPos + 10 > newSize )
		{
			if ( HANglobalReAlloc( newHan, QBYTELEN(newSize + 100) ) )
			{
				newSize += 100;
				newPtr = qHandleTextPtr( newHan, 0 );
			}
			else break;
		}
	}
	HANglobalFree( mDocHan );
	newPtr.dataLen( QBYTELEN(newPos) );
	mDocHan = newHan;
	mDocPtr = newPtr;
}

// AE6479 Starts
void WPHtmlClass::parseText(EXTfldval *fldval,qchar* pSrchWords)
{
	mDocHan = fldval->getHandle( qtrue );
	mDocPtr = qHandleTextPtr( mDocHan, 0 );
	// this doesnt compile on mac : is  it required
	// mDocPtr.dataLen( mDocHan->mDataLen());
	if ( !!mDocPtr && mDocPtr.charLen() > 0 )	// CR0963 : guard against empty handle
		parse(pSrchWords);
}

void WPHtmlClass::parse(qchar* pSrchWords)
{
	if ( !sHTMLtags ) 
	{
		sHTMLtags = new char[ strlen(sSTRING1)+strlen(sSTRING2)+1 ]; *sHTMLtags = 0;
		strcat(sHTMLtags,sSTRING1); strcat(sHTMLtags,sSTRING2);
		sHTMLtagsLen = (qlong) strlen( sHTMLtags );	// rmm64bit5
		sHTMLtagCfu = new CHRconvFromBytes((qbyte *) sHTMLtags, sHTMLtagsLen);
	}
	mTitleStart = 0; mTitleLen = 0;
	qlong index = 0; mStackPtr = 0; mLinkInfo = 0;
	mInPara = mExitPara = mNOBR = mInPre = qfalse;
	mTextInfo.mStyle = WP_DOCINFO_BASESTYLE;
	mTextInfo.mSet = WPTEXT_STYLESET;
	convHTML();
	if ( pSrchWords && *pSrchWords != '\0' ) convHTMLsrch( pSrchWords );
	while ( nextHTMLtag( index ) )
	{
		if ( sTagID == eTagHTML ) parseHTML( index );
	}
}
// AE6479 Ends

void WPHtmlClass::parseDocument( str255& pDocument, qchar* pSrchWords )
{
	str255 document = pDocument;
	mDocURL = pDocument;
	apiToHtmlPath( mDocURL ); // just in case
	htmlToApiPath( document );
	
	EXTfile file; qret e = file.open( document, qtrue, qfalse );
	if ( e == e_ok )
	{
		mDocHan = 0;
		// Start rmm6970: Read up to the first 1k of the data, and look for "charset=utf-8" before "<body>", and assume that is part of a meta tag that indicates UTF-8 - this is not ideal, but it 
		// at least caters for UTF-8 for now.
		EXTfile::FILEconversionType fileEncoding = EXTfile::eFILEconvertFromLatin1Api;	// Default for when we do not recognize encoding (also maintains compatibility with previous docview)
		qbyte buffer[1024];
		qlong actLen;
		if (e_ok == file.read(buffer, 0, 1024, actLen) && actLen > 0)
		{
			CHRconvFromLatin1ApiBytes cfl1(buffer, actLen);
			qchar *add = cfl1.dataPtr();
			qlong len = cfl1.len();
			EXTfldval fval;
			fval.setChar(add, len);
			// Do this using replaceStr to ignore case
			str15 charset(QTEXT("CHARSET=UTF-8"));
			str15 body(QTEXT("<BODY")); // rmm9111: Allow for body possibly having an attribute
			if (fval.replaceStr(charset, charset, qtrue) &&
					fval.replaceStr(body, body, qtrue))
			{	
				qlong metaPos = fval.pos(charset);
				qlong bodyPos = fval.pos(body);
				if (metaPos > 0 && metaPos < bodyPos)
				{
					fileEncoding = EXTfile::eFILEconvertFromUtf8;
				}
			}
		}
		// End rmm6970
		e = file.readCharacterData(mDocHan, fileEncoding);
		if (e == e_ok && !!mDocHan)	// rmm8453
		{
			mDocPtr = qHandleTextPtr( mDocHan, 0 );
			parse(pSrchWords); // AE6479
		}
		else mDocPtr.setNull();
		file.close();
	}
}


void WPHtmlClass::parseHTML( qlong& pInd )
{
	while ( nextHTMLtag( pInd ) )
	{
		if ( sTagID == eTagHEAD )
			parseHTMLhead( pInd );
		else if ( sTagID == eTagBODY )
			parseHTMLbody( pInd );
	}
}


void WPHtmlClass::parseHTMLhead( qlong& pInd )
{
	while ( nextHTMLtag( pInd ) )
	{
		switch ( sTagID )
		{
			case eTagHEAD:
				if ( sIsEndTag ) return;
				break;
			case eTagBODY:						// mpm5013 begins
				parseHTMLbody( pInd );
				return;									// mpm5013 ends
			case eTagTITLE:
				if ( sIsEndTag )
				{
					mTitleLen = pInd - mTitleStart - sTag.length() - 3; // -3 = </ and >
					if (!mPrinting && !mCodeAssistant) // mpm4700 // rmm_emat
					{
						EXTfldval fvals[2];
						fvals[0].setLong( mIdent );
						fvals[1].setChar( &mDocPtr[mTitleStart], mTitleLen );
						ECOsendEvent(mEventHwnd, cWPsetTitleEvent, &fvals[0], 2, qtrue);
					}
				}
				else
				{
					mTitleStart = pInd;
				}
				break;
			case eTagMETA:
				ignoreHTMLtagSimple( pInd );
				break;
		}
	}
}


void WPHtmlClass::parseHTMLbody( qlong& pInd )
{
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagBGCOLOR:
				mDocInfo.mBackColor = getHTMLcolor( sPropValue );
				break;
			case eTagTEXT:
				mDocInfo.mTextColor = getHTMLcolor( sPropValue );
				break;
			case eTagLINK:
				mDocInfo.mLinkColor = getHTMLcolor( sPropValue );
				break;
			case eTagVLINK:
				mDocInfo.mVLinkColor = getHTMLcolor( sPropValue );
				break;
			case eTagALINK:
				mDocInfo.mLinkColor = getHTMLcolor( sPropValue );
				break;
			case eTagLEFTMARGIN:
				mDocInfo.mLeftMargin = getHTMLlong( sPropValue );
				break;
			case eTagTOPMARGIN:
				mDocInfo.mTopMargin = getHTMLlong( sPropValue );
				break;
		}
	}

	mSpareTextPara = 0;									// rmm5079
	while ( nextHTMLtag( pInd, this ) ) // rmm5079
	{
		parseHTMLmain( pInd, this );
	}
}


void WPHtmlClass::parseHTMLpre( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	
	push_fontNum();
	
	qfnt fnt; GDIsetFontName( &fnt, &propCodeFontName[1], propCodeFontName.length() );
	mTextInfo.mFontNum = GDIgetFontNumber( &fnt );
	mTextInfo.mSet |= WPTEXT_FONTNUMSET;
	// Start rmmdocgen
	mTextInfo.mFontSize = mPointSizeForPreAndXmp; // rmm4799
	mTextInfo.mFontSizeMode = eWPFontSizePoints;
	mTextInfo.mSet |= WPTEXT_FONTSIZESET;
	// End rmmdocgen
	
	mInPre = qtrue;
	parseHTMLpara( pInd, pParent );
	mInPre = qfalse;
	
	pop_fontNum();
	
}


void WPHtmlClass::parseHTMLpara( qlong& pInd, WPObjectClass* pParent )
{
	mSpareTextPara = 0;	// rmm5079
	if ( exitPara( pInd ) ) return;
	mInPara = qtrue; mExitPara = qfalse;
	WPParagraphInfoStruct paragraphInfo = mParagraphInfo;
	while ( nextHTMLtagProperty( pInd ) )
	{
		if ( sPropTagID == eTagALIGN )
			paragraphInfo.mHAlign = getHTMLalign( sPropValue );
	}
	// create the paragraph
	WPParagraphClass* para = WPParagraphClass::make( pParent, this, &paragraphInfo );
	parseHTMLpara2( pInd, para );
	mInPara = qfalse;
}


void WPHtmlClass::parseHTMLpara2( qlong& pInd, WPObjectClass* pParent )
{
	sTagHaveBreak = qfalse;
	eTagID theTagID = sTagID;
	ignoreHTMLwhiteSpace( pInd );
	while ( !mExitPara && pInd < (qlong)mDocPtr.charLen() )
	{
		parseHTMLtext( pInd, pParent ); // sTag will be set when returning
		//if ( sIsEndTag && ( sTagID == theTagID || sTagID == eTagP ) )
		//	return;
		if ( sIsEndTag )
		{
			if ( sTagID == theTagID ) return;
			switch ( sTagID )
			{
				case eTagP:
					break; // return;  // mpm5014
				case eTagUL: case eTagOL: case eTagDIR:
				case eTagMENU: case eTagDL:
					pInd--;
					while ( mDocPtr[pInd] != CHAR_OPEN_TAG ) pInd--;
					return;
				default:
					parseHTMLmain( pInd, pParent );
					break;
			}
		}
		else if ( sTagID == theTagID )
		{
			pInd--;
			while ( mDocPtr[pInd] != CHAR_OPEN_TAG ) pInd--;
			return;
		}
		else if ( !parseHTMLmain( pInd, pParent ) && !sTagIsClosed )
			ignoreHTMLtagSimple( pInd );
	}
}


qbool WPHtmlClass::parseHTMLmain( qlong& pInd, WPObjectClass* pParent )
{
	if ( sIsEndTag )
	{
		switch ( sTagID )
		{
			case eTagBIG: case eTagSMALL:
				pop_fontSize();
				break;
			case eTagX:
				mTextInfo.mInvert = qfalse;
				break;
			case eTagB: case eTagSTRONG:
				pop_bold();
				break;
			case eTagI: case eTagEM: case eTagCITE:
				pop_italic();
				break;
			case eTagCODE: case eTagKBD: case eTagSAMP:
			case eTagTT:
				pop_fontNum();
				break;
			case eTagU:
				pop_underline();
				break;
			case eTagS: case eTagSTRIKE:
				pop_strike();
				break;
			case eTagSUB:
				pop_sub();
				break;
			case eTagSUP:
				pop_super();
				break;
			case eTagA:
				parseHTMLlink( pInd, pParent );
				break;
			case eTagDIV:
				parseHTMLdiv( pInd );
				break;
			case eTagFONT:
				parseHTMLfont( pInd );
				break;
			case eTagBLOCKQUOTE:
				mParagraphInfo.mIndent -= WPARA_INDENT_SIZE;
				break;
			case eTagNOBR:
				mNOBR = qfalse;
				break;
			case eTagDFN: case eTagNOFRAMES: // ignore
			case eTagNOSCRIPT: case eTagSPAN:
				break;
			default:
				return qfalse;
		}
	}
	else
	{
		switch ( sTagID )
		{
			case eTagBR:
				if ( !mNOBR )
				{
					sTagHaveBreak = qtrue;
					ignoreHTMLwhiteSpace( pInd );
				}
				break;
			case eTagBIG:
				push_fontSize();
				if ( mTextInfo.mSet & WPTEXT_FONTSIZESET )
					mTextInfo.mFontSize++;
				else
				{
					mTextInfo.mFontSize = 1;
					mTextInfo.mFontSizeMode = eWPFontSizeHtmlAdj;
					mTextInfo.mSet |= WPTEXT_FONTSIZESET;
				}
				break;
			case eTagSMALL:
				push_fontSize();
				if ( mTextInfo.mSet & WPTEXT_FONTSIZESET )
					mTextInfo.mFontSize--;
				else
				{
					mTextInfo.mFontSize = -1;
					mTextInfo.mFontSizeMode = eWPFontSizeHtmlAdj;
					mTextInfo.mSet |= WPTEXT_FONTSIZESET;
				}
				break;
			case eTagX:
				mTextInfo.mInvert = qtrue;
				break;
			case eTagB: case eTagSTRONG:
				push_bold();
				mTextInfo.mFontStyBold = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSTYBSET;
				break;
			case eTagI: case eTagEM: case eTagCITE:
				push_italic();
				mTextInfo.mFontStyItalic = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSTYISET;
				break;
			case eTagCODE: case eTagKBD: case eTagSAMP:
			case eTagTT:
				{
					push_fontNum();
					qfnt fnt; GDIsetFontName( &fnt, &propCodeFontName[1], propCodeFontName.length() );
					mTextInfo.mFontNum = GDIgetFontNumber( &fnt );
					mTextInfo.mSet |= WPTEXT_FONTNUMSET;
				}
				break;
			case eTagU:
				push_underline();
				mTextInfo.mFontStyUnderline = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSTYUSET;
				break;
			case eTagS: case eTagSTRIKE:
				push_strike();
				mTextInfo.mFontStrike = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSTRIKESET;
				break;
			case eTagSUB:
				push_sub();
				mTextInfo.mFontSub = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSUBSET;
				break;
			case eTagSUP:
				push_super();
				mTextInfo.mFontSuper = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSUPERSET;
				break;
			case eTagBLOCKQUOTE:
				mParagraphInfo.mIndent += WPARA_INDENT_SIZE;
				break;
			case eTagPRE: case eTagXMP:
				parseHTMLpre( pInd, pParent );
				break;
			case eTagP:
				parseHTMLpara( pInd, pParent );
				break;
			case eTagUL: case eTagOL: case eTagDIR: case eTagMENU:
				parseHTMLbulletList( pInd, pParent );
				break;
			case eTagDL:
				parseHTMLdefList( pInd, pParent );
				break;
			case eTagTABLE:
				parseHTMLtable( pInd, pParent );
				break;
			case eTagH1: case eTagH2: case eTagH3: case eTagH4: case eTagH5: case eTagH6: 
				parseHTMLparaHeader( pInd, pParent );
				break;
			case eTagLISTING:
				parseHTMLlisting( pInd, pParent );
				break;
			case eTagADDRESS:
				parseHTMLaddress( pInd, pParent );
				break;
			case eTagCENTER:
				parseHTMLcenter( pInd, pParent );
				break;
			case eTagIMG:
				parseHTMLimage( pInd, saveParent(pParent) );
				break;
			case eTagMARQUEE:
				parseHTMLmarquee( pInd, saveParent(pParent) );
				break;
			case eTagXCOMP:
				parseHTMLxcomp( pInd, saveParent(pParent) );
				break;
			case eTagA:
				parseHTMLlink( pInd, saveParent(pParent) );
				break;
			case eTagHR:
				parseHTMLhorzRule( pInd, saveParent(pParent) );
				break;
			case eTagDIV:
				parseHTMLdiv( pInd );
				break;
			case eTagFONT:
				parseHTMLfont( pInd );
				break;
			case eTagBASEFONT:
				parseHTMLbaseFont( pInd );
				break;
			case eTagEVENT:
				parseHTMLevent( pInd );
				break;
			case eTagBASE:
				parseHTMLbaseURL( pInd );
				break;
			case eTagBGSOUND:
				parseHTMLexecTag( pInd );
				break;
			case eTagNOBR:
				mNOBR = qtrue;
				break;
			case eTagDEBUG:
				sTag = qnil; // so we have a line to place a breakpoint
				break;
			case eTagAREA: case eTagComment: case eTagISINDEX:
			case eTagDFN: case eTagCOL: 
			case eTagCOLGROUP: case eTagNEXTID: case eTagNOFRAMES:
			case eTagNOSCRIPT: case eTagPLAINTEXT:
			case eTagSPAN: case eTagTBODY: case eTagTFOOT:
			case eTagTHEAD: case eTagWBR:
				ignoreHTMLtagSimple( pInd );
				break;
			case eTagMAP: case eTagFORM: case eTagIFRAME:
			case eTagFRAME: case eTagFRAMESET: case eTagAPPLET:
			case eTagEMBED: case eTagCOMMENT: case eTagOBJECT:
			case eTagSCRIPT: case eTagVAR:
				ignoreHTMLtagWithEnd( pInd );
				break;
			default:
				return qfalse;
		}
	}
	return qtrue;
}


void WPHtmlClass::parseHTMLparaHeader( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	push_style();
	
	mTextInfo.mStyle = qshort(WP_DOCINFO_HEADER1STYLE + sTag[2] - '0' - 1);
	mTextInfo.mSet |= WPTEXT_STYLESET;
	parseHTMLpara( pInd, pParent );
	
	pop_style();
}


void WPHtmlClass::parseHTMLaddress( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	push_italic();
	
	mTextInfo.mFontStyItalic = qtrue;
	mTextInfo.mSet |= WPTEXT_FONTSTYISET;

	qshort spaceBefore = mParagraphInfo.mSpaceBefore;
	qshort spaceAfter = mParagraphInfo.mSpaceAfter;
	mParagraphInfo.mSpaceBefore = mParagraphInfo.mSpaceAfter = 0;
	parseHTMLpara( pInd, pParent );
	mParagraphInfo.mSpaceBefore = spaceBefore;
	mParagraphInfo.mSpaceAfter = spaceAfter;
	
	pop_italic();
}


void WPHtmlClass::parseHTMLlisting( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	push_fontSize();
	
	mTextInfo.mFontSize = 2;
	mTextInfo.mFontSizeMode = eWPFontSizeHtml;
	mTextInfo.mSet |= WPTEXT_FONTSIZESET;
	parseHTMLpara( pInd, pParent );
	
	pop_fontSize();
}


void WPHtmlClass::parseHTMLcenter( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	qshort spaceBefore = mParagraphInfo.mSpaceBefore;
	qshort spaceAfter = mParagraphInfo.mSpaceAfter;
	eWPAlign align = mParagraphInfo.mHAlign;
	mParagraphInfo.mSpaceBefore = mParagraphInfo.mSpaceAfter = 0;
	mParagraphInfo.mHAlign = eWPHAcenter;
	parseHTMLpara( pInd, pParent );
	mParagraphInfo.mSpaceBefore = spaceBefore;
	mParagraphInfo.mSpaceAfter = spaceAfter;
	mParagraphInfo.mHAlign = align;
}


void WPHtmlClass::parseHTMLbulletList( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	eTagID theTagID = sTagID; qbool isOrdered = sTagID == eTagOL;
	WPParagraphInfoStruct savedInfo = mParagraphInfo, paragraphInfo = mParagraphInfo;
	paragraphInfo.mIndent += WPARA_INDENT_SIZE;
	paragraphInfo.mBullet = isOrdered ? 
														eWPbullet(qshort(eWPbulletNumeral) + mBulletLevel) : 
														eWPbullet(qshort(eWPbulletDisc) + mBulletLevel);
	mBulletLevel++;
	qshort spacing = 1, number = 1; 
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagCOMPACT:
				spacing = 0;
				break;
			case eTagTYPE:
				paragraphInfo.mBullet = isOrdered ? getHTMLbulletType( sPropValueX ) : getHTMLbulletType( sPropValue );
				break;
			case eTagSTART:
				number = (qshort)getHTMLlong( sPropValue );
				break;
		}
	}
	paragraphInfo.mSpaceBefore = paragraphInfo.mSpaceAfter = spacing;
	mParagraphInfo.mSpaceBefore = mParagraphInfo.mSpaceAfter = spacing;
	mParagraphInfo.mIndent = paragraphInfo.mIndent;
	
	qbool first = mBulletLevel == 1; WPParagraphClass* para = 0;
	while ( nextHTMLtag( pInd ) )
	{
		WPParagraphInfoStruct savedInfo2 = paragraphInfo;
		if ( sIsEndTag && sTagID == theTagID )
		{
			mBulletLevel--;
			if ( !mBulletLevel && mBulletClass ) mBulletClass->paragraphInfo().mSpaceAfter = WPARA_SPACING;
			WPTextInfoStruct tInfo = mTextInfo;
			mParagraphInfo = savedInfo;
			mParagraphInfo.mTextInfo = tInfo;
			return;
		}
		else if ( sTagID == eTagLI )
		{
			while ( nextHTMLtagProperty( pInd ) )
			{
				if ( sPropTagID == eTagTYPE )
					paragraphInfo.mBullet = isOrdered ? getHTMLbulletType( sPropValueX ) : getHTMLbulletType( sPropValue );
				else if ( sPropTagID == eTagSTART )
					number = (qshort)getHTMLlong( sPropValue );
			}
			if ( number < 1 ) number = 1;
			paragraphInfo.mNumber = number; number++;
			if ( first ) { paragraphInfo.mSpaceBefore = WPARA_SPACING; first = qfalse; }

			mBulletClass = para = WPParagraphClass::make( pParent, this, &paragraphInfo );
			mExitPara = qfalse;
			parseHTMLpara2( pInd, para );
		}
		else if ( !parseHTMLmain( pInd, pParent ) && !sTagIsClosed )
		{
			ignoreHTMLtagSimple( pInd );
		}
		paragraphInfo = savedInfo2;
	}
}


void WPHtmlClass::parseHTMLdefList( qlong& pInd, WPObjectClass* pParent )
{
	if ( exitPara( pInd ) ) return;
	WPParagraphInfoStruct paragraphInfo = mParagraphInfo;
	WPParagraphInfoStruct mainInfo = mParagraphInfo;
	qshort spacing = 1;
	while ( nextHTMLtagProperty( pInd ) )
	{
		if ( sPropTagID == eTagCOMPACT )
			spacing = 0;
	}
	paragraphInfo.mSpaceBefore = paragraphInfo.mSpaceAfter = spacing;
	
	qbool first = qtrue; WPParagraphClass* para = 0;
	while ( nextHTMLtag( pInd ) )
	{
		WPParagraphInfoStruct savedInfo = paragraphInfo;
		if ( sIsEndTag && sTagID == eTagDL )
		{
			if ( para ) para->paragraphInfo().mSpaceAfter = WPARA_SPACING;
			mParagraphInfo = mainInfo;
			return;
		}
		else if ( !sIsEndTag && ( sTagID == eTagDD || sTagID == eTagDT ) )
		{
			if ( sTagID == eTagDD )
				paragraphInfo.mIndent += WPARA_INDENT_SIZE;

			mParagraphInfo.mIndent = paragraphInfo.mIndent;
			if ( first ) { paragraphInfo.mSpaceBefore = WPARA_SPACING; first = qfalse; }
			para = WPParagraphClass::make( pParent, this, &paragraphInfo );
			mInPara = qtrue; mExitPara = qfalse;
			parseHTMLpara2( pInd, para );
			mInPara = qtrue; mExitPara = qfalse;
		}
		else if ( !parseHTMLmain( pInd, pParent ) && !sTagIsClosed )
		{
			ignoreHTMLtagSimple( pInd );
		}
		/*if ( pParent->lastChild() != para )
		{
			if ( para ) para->paragraphInfo().mSpaceAfter = WPARA_SPACING;
			first = qtrue;
		}*/
		paragraphInfo = savedInfo;
	}
}


void WPHtmlClass::parseHTMLtable( qlong& pInd, WPObjectClass* pParent, WPTableInfoStruct* pTableInfo )
{
	if ( exitPara( pInd ) ) return;
	WPTableInfoStruct tableInfo; if ( pTableInfo ) tableInfo = *pTableInfo;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagWIDTH:
				tableInfo.mWidth = getHTMLwidthHeight( sPropValue );
				break;
			case eTagALIGN:
				tableInfo.mHAlign = getHTMLalign( sPropValue );
				break;
			case eTagVALIGN:
				tableInfo.mVAlign = getHTMLalign( sPropValue );
				break;
			case eTagBGCOLOR:
				tableInfo.mBackColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDER:
				tableInfo.mBorderSize = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagBORDERCOLOR:
				tableInfo.mBorderColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORDARK:
				tableInfo.mBorderColorDark = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORLIGHT:
				tableInfo.mBorderColorLight = getHTMLcolor( sPropValue );
				break;
			case eTagCELLPADDING:
				tableInfo.mCellPadding = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagCELLSPACING:
				tableInfo.mCellSpacing = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagHSPACE:
				tableInfo.mHorzSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagVSPACE:
				tableInfo.mVertSpace = (qldim)getHTMLlong( sPropValue );
				break;
		}
	}
	
	qbool newRow = qtrue;	// rmm6036
	WPTableClass* table = WPTableClass::make( pParent, this, &tableInfo );
	while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag && sTagID == eTagTABLE )
			return;
		else if ( sTagID == eTagTR )
		{
			parseHTMLtableRow( pInd, table, &tableInfo );
			newRow = qtrue;	// rmm6036
		}
		else if ( sTagID == eTagTD )	// rmm6036: allow for TD tag without a table row
		{
			parseHTMLtableCell(pInd, table, newRow, &tableInfo);
			newRow = qfalse;
		}
		else
			parseHTMLmain( pInd, pParent );
	}
}


void WPHtmlClass::parseHTMLtableRow( qlong& pInd, WPTableClass* pParent, WPTableInfoStruct* pTableInfo )
{
	WPTableInfoStruct tableInfo; if ( pTableInfo ) tableInfo = *pTableInfo;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagALIGN:
				tableInfo.mHAlign = getHTMLalign( sPropValue );
				break;
			case eTagVALIGN:
				tableInfo.mVAlign = getHTMLalign( sPropValue );
				break;
			case eTagBGCOLOR:
				tableInfo.mBackColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDER:
				tableInfo.mBorderSize = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagBORDERCOLOR:
				tableInfo.mBorderColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORDARK:
				tableInfo.mBorderColorDark = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORLIGHT:
				tableInfo.mBorderColorLight = getHTMLcolor( sPropValue );
				break;
		}
	}
	qbool first = qtrue;
	while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag && sTagID == eTagTR ) return;
		else if ( sTagID == eTagTD || sTagID == eTagTH )
		{
			parseHTMLtableCell( pInd, pParent, first, &tableInfo );
			first = qfalse;
		}
		else parseHTMLmain( pInd, pParent->parent() );
	}
}


void WPHtmlClass::parseHTMLtableCell( qlong& pInd, WPTableClass* pParent, qbool pStartNewRow, WPTableInfoStruct* pTableInfo )
{
	eTagID theTag = sTagID;
	WPTableCellInfoStruct cellInfo; if ( pTableInfo ) MEMmovel( pTableInfo, &cellInfo, sizeof(WPTableComonInfoStruct) );
	if ( theTag == eTagTH )
	{ // it's a header cell
		push_bold();
		mTextInfo.mFontStyBold = qtrue;
		mTextInfo.mSet |= WPTEXT_FONTSTYBSET;
		cellInfo.mHAlign = eWPHAcenter;
	}
	cellInfo.mWidth = 0;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagWIDTH:
				cellInfo.mWidth = getHTMLwidthHeight( sPropValue );
				break;
			case eTagHEIGHT:
				cellInfo.mHeight = getHTMLwidthHeight( sPropValue );
				break;
			case eTagALIGN:
				cellInfo.mHAlign = getHTMLalign( sPropValue );
				break;
			case eTagVALIGN:
				cellInfo.mVAlign = getHTMLalign( sPropValue );
				break;
			case eTagBGCOLOR:
				cellInfo.mBackColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDER:
				cellInfo.mBorderSize = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagBORDERCOLOR:
				cellInfo.mBorderColor = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORDARK:
				cellInfo.mBorderColorDark = getHTMLcolor( sPropValue );
				break;
			case eTagBORDERCOLORLIGHT:
				cellInfo.mBorderColorLight = getHTMLcolor( sPropValue );
				break;
			case eTagCOLSPAN:
				cellInfo.mColSpan = (qshort)getHTMLlong( sPropValue );
				if ( cellInfo.mColSpan==0 )		//pkst6-00009
					cellInfo.mColSpan = 1;
				break;
			case eTagROWSPAN:
				cellInfo.mRowSpan = (qshort)getHTMLlong( sPropValue );
				break;
			case eTagNOWRAP:
				cellInfo.mNoWrap = qtrue;
				break;
		}
	}
	eWPAlign savedAlign = mParagraphInfo.mHAlign;
	mParagraphInfo.mHAlign = cellInfo.mHAlign;

	WPObjectClass* cell = pParent->addCell( &cellInfo, pStartNewRow );
	WPParagraphClass* cellPara = WPParagraphClass::make( cell, this, &mParagraphInfo );
	mInPara = qtrue; mExitPara = qfalse;
	parseHTMLpara2( pInd, cellPara );
	mInPara = qfalse; mExitPara = qfalse;
	if ( sIsEndTag && sTagID == theTag ) ;
	else while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag && sTagID == theTag ) break;
		if ( sTagID == eTagP && !sIsEndTag )
		{
			sTagID = theTag;
			parseHTMLpara( pInd, cell );
			if ( sIsEndTag && sTagID == theTag ) break;
		}
		else if ( !parseHTMLmain( pInd, cell ) && !sTagIsClosed )
			ignoreHTMLtagSimple( pInd );
		mInPara = qfalse; mExitPara = qfalse;
	}
	if ( cellPara && !cellPara->firstChild() )
	{
		WPObjectClass::remove( cellPara );
		delete cellPara;
	}
	mInPara = qfalse; mExitPara = qfalse;
	if ( cell->firstChild() ) cell->firstChild()->setSpaceBefore(0);
	if ( cell->lastChild() ) cell->lastChild()->setSpaceAfter(0);
	
	mParagraphInfo.mHAlign = savedAlign;

	if ( theTag == eTagTH )
	{ // it's a header cell
		pop_bold();
	}
}


void WPHtmlClass::parseHTMLimage( qlong& pInd, WPObjectClass* pParent )
{
	WPImageInfoStruct imageInfo; str255 src; qbool widthSet = qfalse;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagSRC:
				src = sPropValueX;
				break;
			case eTagHEIGHT:
				imageInfo.mHeight = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagWIDTH:
				imageInfo.mWidth = (qldim)getHTMLlong( sPropValue );
				widthSet=qtrue;	// AE5202
				break;
			case eTagALIGN:
				imageInfo.mAlign = getHTMLalign( sPropValue );
				break;
			case eTagALT:
				imageInfo.mText = sPropValueX;
				break;
			case eTagBORDER:
				imageInfo.mBorderWidth = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagHSPACE:
				imageInfo.mHorzSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagVSPACE:
				imageInfo.mVertSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagDATAOBJTYPE:	// rmm_ncs
				imageInfo.setObjType(sPropValueX);
				break;
		}
	}
	
	imageInfo.mTextInfo = mTextInfo;
	if ( mLinkInfo && !widthSet ) imageInfo.mWidth = 1;
	WPImageClass*	image = WPImageClass::make( pParent, this, &imageInfo, &src[1], src[0] );
	if ( mLinkInfo ) image->setLinkInfo( mLinkInfo );
}


static void sAddProperty( EXTqlist*& pList, strxxx& pPropName, qchar* pPropValue, qlong pPropValueLen )
{
	if ( !pList )
	{
		pList = new EXTqlist(listVlen);
		pList->addCol( fftCharacter, dpFcharacter );
		pList->addCol( fftCharacter, dpFcharacter );
	}
	// replace CHAR_OPEN_TAGs and CHAR_CLOSE_TAGs with characters
	qchar* ptr = &pPropValue[pPropValueLen];
	while ( ptr > pPropValue )
	{
		ptr = MEMdecAddr( ptr, 1 );
		if ( *ptr == CHAR_OPEN_TAG )
			*ptr = '<';
		else if ( *ptr == CHAR_CLOSE_TAG )
			*ptr = '>';
	}
	
	qlong row = pList->insertRow(); EXTfldval fvalp;
	if ( pList->getColValRef( row, 1, fvalp, qtrue ) )
		fvalp.setChar( pPropName );
	if ( pList->getColValRef( row, 2, fvalp, qtrue ) )
		fvalp.setChar( pPropValue, pPropValueLen );
}


void WPHtmlClass::parseHTMLexecTag( qlong& pInd )
{
	EXTqlist*	propList = 0;
	while ( nextHTMLtagProperty( pInd ) )
	{
		sAddProperty( propList, sPropTagX, &sPropValueX[1], sPropValueX[0] );
	}
	
	if (!mPrinting && !mCodeAssistant) // mpm4700 // rmm_emat
	{
		EXTfldval fvals[3];
		fvals[0].setLong( mIdent );
		fvals[1].setChar( sTag );
		fvals[2].setList( propList, qtrue );
		ECOsendEvent(mEventHwnd, cWPexecTagEvent, &fvals[0], 3, qtrue);
	}
	delete propList;	// rmm8448
}


void WPHtmlClass::parseHTMLmarquee( qlong& pInd, WPObjectClass* pParent )
{
	str31 propName, propValue;
	EXTqlist*	propList = 0;
	qcol bgColor = mDocInfo.mBackColor;
	WPXCompInfoStruct xcompInfo;
	xcompInfo.mWidth = -100;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagALIGN:
				xcompInfo.mAlign = getHTMLalign( sPropValue );
				break;
			case eTagBGCOLOR:
				bgColor = getHTMLcolor( sPropValue );
				break;
			case eTagHEIGHT:
				xcompInfo.mHeight = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagHSPACE:
				xcompInfo.mHorzSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagSCROLLAMOUNT:
				propName = str31(QTEXT("$steps"));
				qlongToString( getHTMLlong( sPropValue ), propValue );
				sAddProperty( propList, propName, &propValue[1], propValue[0] );
				break;
			case eTagSCROLLDELAY:
				propName = str31(QTEXT("$speed"));
				qlongToString( getHTMLlong( sPropValue ), propValue );
				sAddProperty( propList, propName, &propValue[1], propValue[0] );
				break;
			case eTagVSPACE:
				xcompInfo.mVertSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagWIDTH:
				xcompInfo.mWidth = (qldim)getHTMLlong( sPropValue );
				break;
		}
	}
	
	qlong start = pInd;
	qbool assigned = qfalse;	// rmm8448
	while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag && sTagID == eTagMARQUEE )
		{
			propName = str31(QTEXT("$message"));
			propValue = str255( qshort(pInd - start - sTag.length() - 3), &mDocPtr[start] );
			sAddProperty( propList, propName, &propValue[1], propValue[0] );
			
			propName = str31(QTEXT("$::backcolor"));
			qlongToString<qlong>( bgColor, propValue ); // rmm64bit1
			sAddProperty( propList, propName, &propValue[1], propValue[0] );

			propName = str31(QTEXT("$::textcolor"));
			qlongToString<qlong>( mTextInfo.getTextColor( this ), propValue ); // rmm64bit1
			sAddProperty( propList, propName, &propValue[1], propValue[0] );

			str80 componentLib(QTEXT("Marquee Library"));
			str80 componentCtrl(QTEXT("Marquee Control"));
			WPXCompClass::make( pParent, this, &xcompInfo, componentLib, componentCtrl, propList );
			assigned = qtrue;	// rmm8448
			break;
		}
	}
	// Start rmm8448
	if (!assigned && propList)
		*propList = qnil;
	delete propList;	
	// End rmm8448
}


void WPHtmlClass::parseHTMLxcomp( qlong& pInd, WPObjectClass* pParent )
{
	WPXCompInfoStruct xcompInfo; qbool apiPath = qfalse, htmlPath = qfalse;
	EXTqlist*	propList = 0; str80 componentLib, componentCtrl;
	while ( nextHTMLtagProperty( pInd ) )
	{
		if ( apiPath || htmlPath )
		{
			fullPath(sPropValueX);
			if ( apiPath ) WPHtmlClass::htmlToApiPath(sPropValueX);
			apiPath = htmlPath = qfalse;
		}	
		
		switch ( sPropTagID )
		{
			case eTagCOMPONENTLIB:
				componentLib = sPropValueX;
				break;
			case eTagCOMPONENTCTRL:
				componentCtrl = sPropValueX;
				break;
			case eTagHEIGHT:
				xcompInfo.mHeight = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagWIDTH:
				xcompInfo.mWidth = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagALIGN:
				xcompInfo.mAlign = getHTMLalign( sPropValue );
				break;
			case eTagHSPACE:
				xcompInfo.mHorzSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagVSPACE:
				xcompInfo.mVertSpace = (qldim)getHTMLlong( sPropValue );
				break;
			case eTagAPIPATH:
				apiPath = qtrue;
				break;
			case eTagHTMLPATH:
				htmlPath = qtrue;
				break;
			default:
			{
				sPropTagX.insert( '$', 1 );
				sAddProperty( propList, sPropTagX, &sPropValueX[1], sPropValueX[0] );
			}
		}
	}
	qlong scriptStart = 0;
	str31 scriptTarget;
	
	while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag )
		{
			if ( sTagID == eTagXCOMP )
			{
				WPXCompClass::make( pParent, this, &xcompInfo, componentLib, componentCtrl, propList );
				delete propList;	// rmm8448
				return;
			}
			else if ( sTagID == eTagSCRIPT && scriptStart )
			{
				qlong scriptLen = pInd - scriptStart - sTag.length() - 3;
				if ( scriptTarget.length() )
					scriptTarget.insert( '$', 1 );
				sAddProperty( propList, scriptTarget, &mDocPtr[scriptStart], scriptLen );
				
				scriptStart = 0;
			}
		}
		else if ( sTagID == eTagSCRIPT )
		{
			while ( nextHTMLtagProperty( pInd ) )
			{
				if ( sPropTagID == eTagTARGET )
					scriptTarget = sPropValueX;
			}
			scriptStart = pInd;
		}
	}
	// Start rmm8448
	if (propList)
		*propList = qnil;
	delete propList;	
	// End rmm8448
}


void WPHtmlClass::parseHTMLlink( qlong& pInd, WPObjectClass* pParent )
{
	if ( !sTagIsClosed )
	{
		mLinkInfo = new WPLinkInfoStruct();
		while ( nextHTMLtagProperty( pInd ) )
		{
			switch ( sPropTagID )
			{
				case eTagHREF:
					mLinkInfo->mHRef = sPropValueX;
					break;
				case eTagNAME:
					mLinkInfo->mName = sPropValueX;
					break;
				case eTagTARGET:
					mLinkInfo->mTarget = sPropValueX;
					break;
				case eTagTITLE:
					mLinkInfo->mTitle = sPropValueX;
					break;
				// Start rmm_emat
				case eTagCLASS:
				{
					mLinkInfo->mColor = getSyntaxColorFromClassName();
					break;
				}
				// End rmm_emat
			}
		}
		if ( !mLinkInfo->mHRef && !!mLinkInfo->mName ) mLinkInfo->mIsBookmark = qtrue;
	}
	else if ( sIsEndTag && mLinkInfo )
	{
		delete mLinkInfo; mLinkInfo = 0;
	}
}


void WPHtmlClass::parseHTMLhorzRule( qlong& pInd, WPObjectClass* pParent )
{
	WPHorzRuleInfoStruct horzRuleInfo;
	while ( nextHTMLtagProperty( pInd ) )
	{
		switch ( sPropTagID )
		{
			case eTagALIGN:
				horzRuleInfo.mHAlign = getHTMLalign( sPropValue );
				break;
			case eTagNOSHADE:
				horzRuleInfo.mNoShade = qtrue;
				break;
			case eTagSIZE:
				horzRuleInfo.mSize = (qldim)getHTMLwidthHeight( sPropValue );
				break;
			case eTagWIDTH:
				horzRuleInfo.mWidth = (qldim)getHTMLwidthHeight( sPropValue );
				break;
		}
	}
	
	WPHorzRule::make( pParent, this, &horzRuleInfo );
}


void WPHtmlClass::parseHTMLtext( qlong& pInd, WPObjectClass* pParent )
{
	qlong start = pInd;
	while ( pInd < (qlong)mDocPtr.charLen() )
	{
		qchar ch = mDocPtr[pInd];
		switch ( ch )
		{
			case 13: // will only get one when mInPre is true 
			case CHAR_OPEN_TAG:
			{
				qbool haveText = qfalse;
				qlong len = pInd - start;
				if ( ch == 13 ) // mpm4723a removed mpm4723 changes 
					haveText = qtrue;
				else
					haveText = len > 0;
				if ( haveText )
				{
					WPTextClass* text = WPTextClass::make( pParent, this, &mTextInfo, start, pInd - start );
					if ( sTagHaveBreak ) { text->setBreak( qtrue ); sTagHaveBreak = qfalse; }
					if ( mLinkInfo ) text->setLinkInfo( mLinkInfo );
				}
				if ( ch == 13 )
				{
					sTagHaveBreak = qtrue;
					start = pInd + 1;
				}
				else
				{
					pInd--;
					nextHTMLtag( pInd );
					return;
				}
			}
		}
		pInd++;
	}
}


// Start rmm_emat: Special syntax colouring class names
static qbool sSyntaxColorClassesInit = qfalse;
static std::unordered_map<std::string,qcol> sSyntaxColorClasses;
// End rmm_emat

void WPHtmlClass::parseHTMLdiv( qlong& pInd )
{
	if ( sIsEndTag )
	{
		pop_halign();
		pop_textColor(); // rmm_emat
		mSpareTextPara = 0; // rmm5079
	}
	else
	{
		push_textColor(); // rmm_emat
		push_halign();
		while ( nextHTMLtagProperty( pInd ) )
		{
			if ( sPropTagID == eTagALIGN )
				mParagraphInfo.mHAlign = getHTMLalign( sPropValue );
			// Start rmm_emat
			else if (sPropTagID == eTagCLASS)
			{
				qcol col = getSyntaxColorFromClassName();
				if (col != colNone)
				{
					mTextInfo.mTextColor = col;
					mTextInfo.mSet |= WPTEXT_TEXTCOLORSET;
				}
			}
			// End rmm_emat
		}
	}
}

// rmm_emat:
qcol WPHtmlClass::getSyntaxColorFromClassName()
{
	// Initialise map - can use a static initializer on Windows, but not on the Mac
	if (!sSyntaxColorClassesInit)
	{
		sSyntaxColorClasses["OMSC_BADSYNTAX"] = GDI_COLOR_OMNIS_CHROMA_BAD_SYNTAX;
		sSyntaxColorClasses["OMSC_CLASSVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_CLASS_VAR;
		sSyntaxColorClasses["OMSC_COMMENT"] = GDI_COLOR_OMNIS_CHROMA_COMMENT;
		sSyntaxColorClasses["OMSC_CONSTANT"] = GDI_COLOR_OMNIS_CHROMA_CONSTANT;
		sSyntaxColorClasses["OMSC_CTRLKEYWORD"] = GDI_COLOR_OMNIS_CHROMA_CTRLKEYWORD;
		sSyntaxColorClasses["OMSC_EVENTPARAMETER"] = GDI_COLOR_OMNIS_CHROMA_EVPARM;
		sSyntaxColorClasses["OMSC_FUNCTION"] = GDI_COLOR_OMNIS_CHROMA_FUNCTION;
		sSyntaxColorClasses["OMSC_HASHVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_HASHVAR;
		sSyntaxColorClasses["OMSC_INSTANCEVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_INSTANCE_VAR;
		sSyntaxColorClasses["OMSC_KEYWORD"] = GDI_COLOR_OMNIS_CHROMA_KEYWORD;
		sSyntaxColorClasses["OMSC_LOCALVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_LOCALVAR;
		sSyntaxColorClasses["OMSC_NOTATION"] = GDI_COLOR_OMNIS_CHROMA_NOTATION;
		sSyntaxColorClasses["OMSC_OPTION"] = GDI_COLOR_OMNIS_CHROMA_OPTION;
		sSyntaxColorClasses["OMSC_PARAMETERVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_PARAMETER_VAR;
		sSyntaxColorClasses["OMSC_RED"] = GDI_COLOR_OMNIS_THEME_RED;
		sSyntaxColorClasses["OMSC_STRING"] = GDI_COLOR_OMNIS_CHROMA_STRING;
		sSyntaxColorClasses["OMSC_TASKVARIABLE"] = GDI_COLOR_OMNIS_CHROMA_TASK_VAR;
		sSyntaxColorClasses["OMSC_VARIABLE"] = GDI_COLOR_OMNIS_CHROMA_VARIABLE;
		sSyntaxColorClasses["OMSC_WINDOWTEXT"] = GDI_COLOR_WINDOWTEXT;

		sSyntaxColorClassesInit = qtrue;
	}
	// Special syntax colouring class names
	std::string classString;
	CHRconvToUtf8 ctu(&sPropValue[1], sPropValue.length());
	classString.assign((char *)ctu.dataPtr(), ctu.len());

	std::unordered_map<std::string, qcol>::const_iterator it = sSyntaxColorClasses.find(classString);
	if (it != sSyntaxColorClasses.end())
		return it->second;
	return colNone;
}

void WPHtmlClass::parseHTMLfont( qlong& pInd )
{
	if ( sIsEndTag )
	{
		pop_fontNum();
		pop_fontSize();
		pop_textColor();
	}
	else
	{
		push_textColor();
		push_fontSize();
		push_fontNum();
		while ( nextHTMLtagProperty( pInd ) )
		{
			if ( sPropTagID == eTagCOLOR )
			{
				mTextInfo.mTextColor = getHTMLcolor( sPropValue );
				mTextInfo.mSet |= WPTEXT_TEXTCOLORSET;
			}
			else if ( sPropTagID == eTagSIZE )
			{
				if ( sPropValue[1] == '+' || sPropValue[1] == '-' )
					mTextInfo.mFontSizeMode = eWPFontSizeHtmlAdj;
				else
					mTextInfo.mFontSizeMode = eWPFontSizeHtml;
				mTextInfo.mFontSize = (qshort)getHTMLlong( sPropValue );
				mTextInfo.mSet |= WPTEXT_FONTSIZESET;
			}
			else if ( sPropTagID == eTagFACE )
			{
				str255 face;
				while ( sPropValueX.length() )
				{
					qshort p = sPropValueX.pos( ',' );
					if ( p )
					{
						face.copy( sPropValueX, 1, p-1 );
						sPropValueX.deleet( 1, p );
					}
					else
					{
						face = sPropValueX;
						sPropValueX = qnil;
					}
					while ( face[1] == ' ' ) face.deleet(1,1);
					while ( face[face.length()] == ' ' ) face[0]--;
					qfnt tmpFnt; GDIsetFontName( &tmpFnt, &face[1], face.length() );
					str255 face2; face2[0] = (qchar)GDIgetFontName( &tmpFnt, &face2[1], 255 );
					if ( face.uprCmp( face2 ) == 0 )
					{
						mTextInfo.mFontNum = GDIgetFontNumber( &tmpFnt );
						mTextInfo.mSet |= WPTEXT_FONTNUMSET;
					}
					break;
				}
			} // else if ( sPropTag == propFontFace )
		} // end while ( nextHTMLtagProperty( pInd ) )
	} // end else
}


void WPHtmlClass::parseHTMLbaseFont( qlong& pInd )
{
	while ( nextHTMLtagProperty( pInd ) )
	{
		if ( sPropTagID == eTagSIZE )
		{
			WPTextInfoStruct newStyle = mDocInfo.mStyles[WP_DOCINFO_BASESTYLE];
			newStyle.mFontSize = (qshort)getHTMLlong( sPropValue );
			mTextInfo.mStyle = mDocInfo.addStyle( &newStyle );
			mTextInfo.mSet |= WPTEXT_STYLESET;
		}
	}
}


void WPHtmlClass::parseHTMLevent( qlong& pInd )
{
	if (!mPrinting && !mCodeAssistant) // mpm4700 // rmm_emat
	{
		EXTfldval fvals[3];
		fvals[0].setLong( mIdent );
		qbool apiPath = qfalse, htmlPath = qfalse;
		while ( nextHTMLtagProperty( pInd ) )
		{
			if ( apiPath || htmlPath )
			{
				fullPath(sPropValueX);
				if ( apiPath ) WPHtmlClass::htmlToApiPath(sPropValueX);
				apiPath = htmlPath = qfalse;
			}	
	
			switch ( sPropTagID )
			{
				case eTagNAME:
					fvals[1].setChar( sPropValueX );
					break;
				case eTagVALUE:
					fvals[2].setChar( sPropValueX );
					break;
				case eTagAPIPATH:
					apiPath = qtrue;
					break;
				case eTagHTMLPATH:
					htmlPath = qtrue;
					break;
			}
		}
		
		ECOsendEvent(mEventHwnd, cWPeventTagEvent, &fvals[0], 3, qtrue);
	}
}


void WPHtmlClass::parseHTMLbaseURL( qlong& pInd )
{
	while ( nextHTMLtagProperty( pInd ) )
	{
		if ( sPropTagID == eTagHREF )
			mDocURL = sPropValueX;
	}
}


void WPHtmlClass::ignoreHTMLwhiteSpace( qlong& pInd )
{
	if ( !mInPre ) while ( pInd < (qlong)mDocPtr.charLen() && mDocPtr[pInd] >= CHAR_CLOSE_TAG && mDocPtr[pInd] <= ' ' ) pInd++;
}


void WPHtmlClass::ignoreHTMLtagSimple( qlong& pInd )
{
	if ( !sTagIsClosed )
	{
		while ( pInd < (qlong)mDocPtr.charLen() && mDocPtr[pInd] != CHAR_CLOSE_TAG ) pInd++;
		pInd++;
	}
}


void WPHtmlClass::ignoreHTMLtagWithEnd( qlong& pInd )
{
	eTagID theTagID = sTagID;
	while ( nextHTMLtag( pInd ) )
	{
		if ( sIsEndTag && theTagID == sTagID ) return;
	}
}



// ################# html parsing utilities ######################################
// ###############################################################################


qbool WPHtmlClass::nextHTMLtag( qlong& pInd, WPObjectClass *pParentForSpareText ) // rmm5079
{
	qlong baseInd = pInd;						// rmm5079
	qbool addedSpareText = qfalse;	// rmm5079
	while ( pInd < (qlong)mDocPtr.charLen() )
	{
		if ( mDocPtr[pInd] == CHAR_OPEN_TAG )
		{
			// Start rmm5079
			if (pParentForSpareText && (pInd - baseInd))
			{
				addParaForSpareText(pParentForSpareText, baseInd, pInd - baseInd);
				addedSpareText = qtrue;
			}
			// End rmm5079
			pInd++;
			qlong start = pInd;
			while ( pInd < (qlong)mDocPtr.charLen() )
			{
				if ( mDocPtr[pInd] == ' ' || mDocPtr[pInd] == 13 || mDocPtr[pInd] == CHAR_CLOSE_TAG )
				{
					sTagIsClosed = mDocPtr[pInd] == CHAR_CLOSE_TAG;
					sIsEndTag = mDocPtr[start] == '/';
					if ( sIsEndTag ) start++;
					sTag = str15( qshort(pInd - start), &mDocPtr[start] );
					if ( sTag[1] == '!' && sTag[2] == '-' && sTag[3] == '-' )
					{
						sTag[0] = 3;
						pInd = start + 3;
					}
					else
					{
						sTag.upps();
						pInd++;
					}
					sTagID = sConvHTMLTag( sTag );
					return qtrue;
				}
				pInd++;
			}
		}
		pInd++;
	}
	// Start rmm5079
	if (pParentForSpareText && (pInd - baseInd) && !addedSpareText)
	{
		addParaForSpareText(pParentForSpareText, baseInd, pInd - baseInd);
	}
	// End rmm5079
	return qfalse;
}


qbool WPHtmlClass::nextHTMLtagProperty( qlong& pInd )
{
	if ( sTagIsClosed ) return qfalse;
	sPropTag = qnil; sPropValue = qnil;
	qbool inName = qfalse;
	qbool inValue = qfalse;
	qbool inQuote = qfalse;
	while ( pInd < (qlong)mDocPtr.charLen() )
	{
		if ( mDocPtr[pInd] == CHAR_CLOSE_TAG )
		{
			sTagIsClosed = qtrue;
			break;
		}
		else if ( mDocPtr[pInd] == '=' )
		{
			inValue = qtrue;
		}
		//pkst6-00009
		else if ( mDocPtr[pInd] == '\'' && inValue )
		{
			if ( inQuote )
				break;
			else
				inQuote = qtrue;
		}
		else if ( mDocPtr[pInd] == '"' && inValue )
		{
			if ( inQuote )
				break;
			else
				inQuote = qtrue;
		}
		else if ( mDocPtr[pInd] == ' ' || mDocPtr[pInd] == 13 )
		{
			if ( inValue )
			{
				if ( inQuote )
					sPropValue.concat( mDocPtr[pInd] );
				else
					break;
			}
			inName = qfalse;
		}
		else if ( inValue )
		{
			sPropValue.concat( mDocPtr[pInd] );
		}
		else
		{
			if ( sPropTag.length() && !inName ) { pInd--; break; } // name without value
			sPropTag.concat( mDocPtr[pInd] );
			inName = qtrue;
		}
		pInd++;
	}
	pInd++;
	sPropTagX = sPropTag;
	sPropTag.upps();
	sPropValueX = sPropValue;
	sPropValue.upps();
	sPropTagID = sConvHTMLTag( sPropTag );
	return sPropTag.length() > 0;
}

// AE6315
// rmm5079: method to add body text as a paragraph, when the text is not wrapped in a paragraph or other tags
// Non-breaking space
#ifdef ismac
	#define HTML_NBSP 0xca
#else
	#define HTML_NBSP 0xa0
#endif
void WPHtmlClass::addParaForSpareText(WPObjectClass *pParentForSpareText, qlong pInd, qlong pLen)
{
	#ifdef _DEBUG
		// Make it easier to see the data we are adding when debugging.
		qchar *dataBeingAdded = new qchar[pLen + 1];
		MEMmovel(&mDocPtr[pInd], dataBeingAdded, pLen);
		dataBeingAdded[pLen] = 0;
		delete [] dataBeingAdded;
	#endif
	// First, make sure we ignore single spaces (inserted by convHTML for CRLF), and non-breaking spaces.
	if (1 == pLen && (' ' == mDocPtr[pInd] || HTML_NBSP == mDocPtr[pInd]))
		return;
	// Also, strip any leading spaces (inserted by convHTML for CRLF)
	while (pLen)
	{
		if (' ' != mDocPtr[pInd])
			break;
		++pInd;
		--pLen;
	}

	// Create a paragraph if one is needed
	if (!mSpareTextPara)
	{
		WPParagraphInfoStruct paragraphInfo = mParagraphInfo;
		mSpareTextPara = WPParagraphClass::make(pParentForSpareText, this, &paragraphInfo);
	}
	// Add the text to the paragraph, and break the line if necessary
	WPTextClass *text = WPTextClass::make(mSpareTextPara, this, &mTextInfo, pInd, pLen);
	if (sTagHaveBreak) 
	{ 
		text->setBreak(qtrue); 
		sTagHaveBreak = qfalse; 
	}
}

static qoschar* sColNames[] = 
{
	QTEXT("ALICEBLUE"),QTEXT("#F0F8FF"), 
	QTEXT("ANTIQUEWHITE"),QTEXT("#FAEBD7"), 
	QTEXT("AQUA"),QTEXT("#00FFFF"), 
	QTEXT("AQUAMARINE"),QTEXT("#7FFFD4"), 
	QTEXT("AZURE"),QTEXT("#F0FFFF"), 
	QTEXT("BEIGE"),QTEXT("#F5F5DC"), 
	QTEXT("BISQUE"),QTEXT("#FFE4C4"), 
	QTEXT("BLACK"),QTEXT("#000000"), 
	QTEXT("BLANCHEDALMOND"),QTEXT("#FFEBCD"), 
	QTEXT("BLUE"),QTEXT("#0000FF"), 
	QTEXT("BLUEVIOLET"),QTEXT("#8A2BE2"), 
	QTEXT("BROWN"),QTEXT("#A52A2A"), 
	QTEXT("BURLYWOOD"),QTEXT("#DEB887"), 
	QTEXT("CADETBLUE"),QTEXT("#5F9EA0"), 
	QTEXT("CHARTREUSE"),QTEXT("#7FFF00"), 
	QTEXT("CHOCOLATE"),QTEXT("#D2691E"), 
	QTEXT("CORAL"),QTEXT("#FF7F50"), 
	QTEXT("CORNFLOWERBLUE"),QTEXT("#6495ED"), 
	QTEXT("CORNSILK"),QTEXT("#FFF8DC"), 
	QTEXT("CRIMSON"),QTEXT("#DC143C"), 
	QTEXT("CYAN"),QTEXT("#00FFFF"), 
	QTEXT("DARKBLUE"),QTEXT("#00008B"), 
	QTEXT("DARKCYAN"),QTEXT("#008B8B"), 
	QTEXT("DARKGOLDENROD"),QTEXT("#B8860B"), 
	QTEXT("DARKGRAY"),QTEXT("#A9A9A9"), 
	QTEXT("DARKGREEN"),QTEXT("#006400"), 
	QTEXT("DARKKHAKI"),QTEXT("#BDB76B"), 
	QTEXT("DARKMAGENTA"),QTEXT("#8B008B"), 
	QTEXT("DARKOLIVEGREEN"),QTEXT("#556B2F"), 
	QTEXT("DARKORANGE"),QTEXT("#FF8C00"), 
	QTEXT("DARKORCHID"),QTEXT("#9932CC"), 
	QTEXT("DARKRED"),QTEXT("#8B0000"), 
	QTEXT("DARKSALMON"),QTEXT("#E9967A"), 
	QTEXT("DARKSEAGREEN"),QTEXT("#8FBC8F"), 
	QTEXT("DARKSLATEBLUE"),QTEXT("#483D8B"), 
	QTEXT("DARKSLATEGRAY"),QTEXT("#2F4F4F"), 
	QTEXT("DARKTURQUOISE"),QTEXT("#00CED1"), 
	QTEXT("DARKVIOLET"),QTEXT("#9400D3"), 
	QTEXT("DEEPPINK"),QTEXT("#FF1493"), 
	QTEXT("DEEPSKYBLUE"),QTEXT("#00BFFF"), 
	QTEXT("DIMGRAY"),QTEXT("#696969"), 
	QTEXT("DODGERBLUE"),QTEXT("#1E90FF"), 
	QTEXT("FELDSPAR"),QTEXT("#D19275"), 
	QTEXT("FIREBRICK"),QTEXT("#B22222"), 
	QTEXT("FLORALWHITE"),QTEXT("#FFFAF0"), 
	QTEXT("FORESTGREEN"),QTEXT("#228B22"), 
	QTEXT("FUCHSIA"),QTEXT("#FF00FF"), 
	QTEXT("GAINSBORO"),QTEXT("#DCDCDC"), 
	QTEXT("GHOSTWHITE"),QTEXT("#F8F8FF"), 
	QTEXT("GOLD"),QTEXT("#FFD700"), 
	QTEXT("GOLDENROD"),QTEXT("#DAA520"), 
	QTEXT("GRAY"),QTEXT("#808080"), 
	QTEXT("GREEN"),QTEXT("#008000"), 
	QTEXT("GREENYELLOW"),QTEXT("#ADFF2F"), 
	QTEXT("HONEYDEW"),QTEXT("#F0FFF0"), 
	QTEXT("HOTPINK"),QTEXT("#FF69B4"), 
	QTEXT("INDIANRED"),QTEXT(" #CD5C5C"), 
	QTEXT("INDIGO"),QTEXT(" #4B0082"), 
	QTEXT("IVORY"),QTEXT("#FFFFF0"), 
	QTEXT("KHAKI"),QTEXT("#F0E68C"), 
	QTEXT("LAVENDER"),QTEXT("#E6E6FA"), 
	QTEXT("LAVENDERBLUSH"),QTEXT("#FFF0F5"), 
	QTEXT("LAWNGREEN"),QTEXT("#7CFC00"), 
	QTEXT("LEMONCHIFFON"),QTEXT("#FFFACD"), 
	QTEXT("LIGHTBLUE"),QTEXT("#ADD8E6"), 
	QTEXT("LIGHTCORAL"),QTEXT("#F08080"), 
	QTEXT("LIGHTCYAN"),QTEXT("#E0FFFF"), 
	QTEXT("LIGHTGOLDENRODYELLOW"),QTEXT("#FAFAD2"), 
	QTEXT("LIGHTGREY"),QTEXT("#D3D3D3"), 
	QTEXT("LIGHTGREEN"),QTEXT("#90EE90"), 
	QTEXT("LIGHTPINK"),QTEXT("#FFB6C1"), 
	QTEXT("LIGHTSALMON"),QTEXT("#FFA07A"), 
	QTEXT("LIGHTSEAGREEN"),QTEXT("#20B2AA"), 
	QTEXT("LIGHTSKYBLUE"),QTEXT("#87CEFA"), 
	QTEXT("LIGHTSLATEBLUE"),QTEXT("#8470FF"), 
	QTEXT("LIGHTSLATEGRAY"),QTEXT("#778899"), 
	QTEXT("LIGHTSTEELBLUE"),QTEXT("#B0C4DE"), 
	QTEXT("LIGHTYELLOW"),QTEXT("#FFFFE0"), 
	QTEXT("LIME"),QTEXT("#00FF00"), 
	QTEXT("LIMEGREEN"),QTEXT("#32CD32"), 
	QTEXT("LINEN"),QTEXT("#FAF0E6"), 
	QTEXT("MAGENTA"),QTEXT("#FF00FF"), 
	QTEXT("MAROON"),QTEXT("#800000"), 
	QTEXT("MEDIUMAQUAMARINE"),QTEXT("#66CDAA"), 
	QTEXT("MEDIUMBLUE"),QTEXT("#0000CD"), 
	QTEXT("MEDIUMORCHID"),QTEXT("#BA55D3"), 
	QTEXT("MEDIUMPURPLE"),QTEXT("#9370D8"), 
	QTEXT("MEDIUMSEAGREEN"),QTEXT("#3CB371"), 
	QTEXT("MEDIUMSLATEBLUE"),QTEXT("#7B68EE"), 
	QTEXT("MEDIUMSPRINGGREEN"),QTEXT("#00FA9A"), 
	QTEXT("MEDIUMTURQUOISE"),QTEXT("#48D1CC"), 
	QTEXT("MEDIUMVIOLETRED"),QTEXT("#C71585"), 
	QTEXT("MIDNIGHTBLUE"),QTEXT("#191970"), 
	QTEXT("MINTCREAM"),QTEXT("#F5FFFA"), 
	QTEXT("MISTYROSE"),QTEXT("#FFE4E1"), 
	QTEXT("MOCCASIN"),QTEXT("#FFE4B5"), 
	QTEXT("NAVAJOWHITE"),QTEXT("#FFDEAD"), 
	QTEXT("NAVY"),QTEXT("#000080"), 
	QTEXT("OLDLACE"),QTEXT("#FDF5E6"), 
	QTEXT("OLIVE"),QTEXT("#808000"), 
	QTEXT("OLIVEDRAB"),QTEXT("#6B8E23"), 
	QTEXT("ORANGE"),QTEXT("#FFA500"), 
	QTEXT("ORANGERED"),QTEXT("#FF4500"), 
	QTEXT("ORCHID"),QTEXT("#DA70D6"), 
	QTEXT("PALEGOLDENROD"),QTEXT("#EEE8AA"), 
	QTEXT("PALEGREEN"),QTEXT("#98FB98"), 
	QTEXT("PALETURQUOISE"),QTEXT("#AFEEEE"), 
	QTEXT("PALEVIOLETRED"),QTEXT("#D87093"), 
	QTEXT("PAPAYAWHIP"),QTEXT("#FFEFD5"), 
	QTEXT("PEACHPUFF"),QTEXT("#FFDAB9"), 
	QTEXT("PERU"),QTEXT("#CD853F"), 
	QTEXT("PINK"),QTEXT("#FFC0CB"), 
	QTEXT("PLUM"),QTEXT("#DDA0DD"), 
	QTEXT("POWDERBLUE"),QTEXT("#B0E0E6"), 
	QTEXT("PURPLE"),QTEXT("#800080"), 
	QTEXT("RED"),QTEXT("#FF0000"), 
	QTEXT("ROSYBROWN"),QTEXT("#BC8F8F"), 
	QTEXT("ROYALBLUE"),QTEXT("#4169E1"), 
	QTEXT("SADDLEBROWN"),QTEXT("#8B4513"), 
	QTEXT("SALMON"),QTEXT("#FA8072"), 
	QTEXT("SANDYBROWN"),QTEXT("#F4A460"), 
	QTEXT("SEAGREEN"),QTEXT("#2E8B57"), 
	QTEXT("SEASHELL"),QTEXT("#FFF5EE"), 
	QTEXT("SIENNA"),QTEXT("#A0522D"), 
	QTEXT("SILVER"),QTEXT("#C0C0C0"), 
	QTEXT("SKYBLUE"),QTEXT("#87CEEB"), 
	QTEXT("SLATEBLUE"),QTEXT("#6A5ACD"), 
	QTEXT("SLATEGRAY"),QTEXT("#708090"), 
	QTEXT("SNOW"),QTEXT("#FFFAFA"), 
	QTEXT("SPRINGGREEN"),QTEXT("#00FF7F"), 
	QTEXT("STEELBLUE"),QTEXT("#4682B4"), 
	QTEXT("TAN"),QTEXT("#D2B48C"), 
	QTEXT("TEAL"),QTEXT("#008080"), 
	QTEXT("THISTLE"),QTEXT("#D8BFD8"), 
	QTEXT("TOMATO"),QTEXT("#FF6347"), 
	QTEXT("TURQUOISE"),QTEXT("#40E0D0"), 
	QTEXT("VIOLET"),QTEXT("#EE82EE"), 
	QTEXT("VIOLETRED"),QTEXT("#D02090"), 
	QTEXT("WHEAT"),QTEXT("#F5DEB3"), 
	QTEXT("WHITE"),QTEXT("#FFFFFF"), 
	QTEXT("WHITESMOKE"),QTEXT("#F5F5F5"), 
	QTEXT("YELLOW"),QTEXT("#FFFF00"), 
	QTEXT("YELLOWGREEN"),QTEXT("#9ACD32"), 
	0,0
};

qcol WPHtmlClass::getHTMLcolor( strxxx& pVal )
{
	qlong lred = 0, lgreen = 0, lblue = 0; // MHn0085
	if ( pVal[1] == '#' ) // color is in RGB value
	{
		qchar		nibbles[6];
		qchar*	nib = &nibbles[0];
		for ( qshort cnt = 2 ; cnt <= 7 ; cnt++ )
		{
			qchar ch = pVal[cnt];
			*nib = ch >= 'A' ? ch - 'A' + 10 : ch - '0';
			nib++;
		}
		// MHn0085 begins. 
		lred = (qlong)nibbles[0];
		lred = (lred * 16) + (qlong)nibbles[1];
		lgreen = (qlong)nibbles[2];
		lgreen = (lgreen * 16) + (qlong)nibbles[3];
		lblue = (qlong)nibbles[4];
		lblue = (lblue * 16) + (qlong)nibbles[5];  
		return GDImakeQcol( lred,lgreen,lblue );
		// MHn0085 ends.

		//return GDImakeQcol( nibbles[0] * 16 + nibbles[1], nibbles[2] * 16 + nibbles[3], nibbles[4] * 16 + nibbles[5] );
	}
	else
	{
		// AE6315 lookup color names
		qshort name = 0;
		pVal.upps(); str255 s;
		while ( sColNames[name] )
		{
			s = str255( sColNames[name] );
			if ( s.compare(pVal)==0 )
			{
				s = str255( sColNames[name+1] );
				return getHTMLcolor(s);
			} 
			name+=2;
		}
		// Name not found
		return GDI_COLOR_QBLACK;
	}
}


qlong WPHtmlClass::getHTMLlong( strxxx& pVal )
{
	qlong x; stringToQlong( pVal, x );
	return x;
}


qldim WPHtmlClass::getHTMLwidthHeight( strxxx& pVal )
{
	qshort p = pVal.pos('%');
	if ( p ) pVal.deleet( p, 255 );
	qlong x; stringToQlong( pVal, x );
	if ( p )
		return -x;
	else
		return x;
}


eWPAlign WPHtmlClass::getHTMLalign( strxxx& pVal )
{
	eTagID id = sConvHTMLTag( pVal );
	switch ( id )
	{
		case eTagTOP:
			return eWPVAtop;
		case eTagMIDDLE:
			return eWPVAmiddle;
		case eTagBOTTOM:
			return eWPVAbottom;
		case eTagTEXTTOP:
			return eWPVAtexttop;
		case eTagABSMIDDLE:
			return eWPVAabsmiddle;
		case eTagABSBOTTOM:
			return eWPVAabsbottom;
		case eTagBASELINE:
			return eWPVAbaseline;
		case eTagLEFT:
			return eWPHAleft;
		case eTagCENTER:
			return eWPHAcenter;
		case eTagRIGHT:
			return eWPHAright;
		case eTagOMNIS:	// rmm6171
			return eWPHAomnis;
		default:
			return eWPHAleft;
	}
}


eWPbullet WPHtmlClass::getHTMLbulletType( strxxx& pVal )
{
	if ( pVal.length() == 1 )
	{
		switch ( pVal[1] )
		{
			case 'A': return eWPbulletUppCase;
			case 'a': return eWPbulletLowCase;
			case 'I': return eWPbulletUppCaseRoman;
			case 'i': return eWPbulletLowCaseRoman;
			case '1': return eWPbulletNumeral;
			default: return eWPbulletNumeral;
		}
	}
	else
	{
		eTagID id = sConvHTMLTag( pVal );
		switch ( id )
		{
			case eTagCIRCLE:
				return eWPbulletCircle;
			case eTagDISC:
				return eWPbulletDisc;
			case eTagSQUARE:
				return eWPbulletSquare;
			default:
				return eWPbulletDisc;
		}
	}
}



// ###############################################################################
// ################# path/name manipulation ######################################
// ###############################################################################

#if defined(ismac) && !defined(isCOCOA)			// rmm8567
	#define dirSep ':'
	const str15 driveSep(QTEXT(":"));
	const str15 htmlFile(QTEXT("file:///"));
#elif defined(isunix) || defined(isCOCOA)		// rmm8567
	#define dirSep '/'
	const str15 driveSep(QTEXT("/"));
	const str15 htmlFile(QTEXT("file://"));
#else
	#define dirSep '\\'
	const str15 driveSep(QTEXT(":\\"));
	const str15 htmlFile(QTEXT("file:///"));
#endif

#define htmlSep '/'
#define htmlBookmarkSep '#'
#define htmlHexCharMarker '%'
const str15 htmlBack(QTEXT(".."));
const str15 htmlWWW(QTEXT("www."));
const str15 htmlHTTP(QTEXT("http:"));
const str15 htmlCurDir(QTEXT("./"));	// rmmdocgen
const str15 htmlUpOne(QTEXT("../"));	// rmmdocgen

qbool WPHtmlClass::fullPath(str255& pPath, qbool pApi) // rmm10755
{
	if (pPath.pos(htmlWWW) || pPath.pos(htmlHTTP) || pPath.pos(htmlFile)) return qtrue;
	
	str255 docPath = mDocURL;
	// Start rmm10755
	if (mCodeAssistant)
	{
		htmlToApiPath(docPath);
		apiToHtmlPath(docPath);
	}
	// End rmm10755
	if ( pPath[1] == htmlBookmarkSep )
	{
		qshort p = docPath.pos('#');
		if ( p ) docPath.deleet( p, 255 );
		pPath.insert( docPath, 1 );
	}
	else
	{
		extractPath( docPath );
		appendPath( docPath, pPath );
		pPath = docPath;
	}
	// Start rmm10755: If pApi, convert to API path with bookmark
	if (pApi)
	{
		str255 bookmark;
		qshort hp = pPath.pos(htmlBookmarkSep);
		if (hp)
		{
			bookmark.set(&pPath[hp], pPath.length() - (hp - 1));
			pPath[0] = (hp - 1);
		}
		htmlToApiPath(pPath);
		pPath.concat(bookmark);
	}
	// End rmm10755
	return qtrue;
}


qbool WPHtmlClass::getBookmark( str255& pBookmark )
{
	qshort p = pBookmark.pos( htmlBookmarkSep );
	if ( p )
	{
		pBookmark.deleet( 1, p );
		return qtrue;
	}	
	return qfalse;
}


qbool WPHtmlClass::apiToHtmlPath( str255& pPath )
{
	if ( pPath.pos( htmlWWW ) || pPath.pos( htmlHTTP ) || pPath.pos( htmlFile ) ) return qtrue;

	str255 newPath;
	qshort cur = 1;
	while ( cur <= pPath.length() )
	{
		if ( pPath[cur] == dirSep )
			newPath.concat( htmlSep );
		else
			newPath.concat( pPath[cur] );
		cur++;
	}
	newPath.insert( htmlFile, 1 );
	pPath = newPath;
	return qtrue;
}

qbool WPHtmlClass::htmlToApiPath( str255& pPath )
{
	if ( pPath.pos( htmlWWW ) || pPath.pos( htmlHTTP ) ) return qfalse;
	if ( pPath.pos( htmlFile ) == 1 ) pPath.deleet( 1, htmlFile.length() );
	
	str255 newPath; qshort cur = 1;
	while ( cur <= pPath.length() )
	{
		qchar ch = pPath[cur];
		if ( ch == htmlBookmarkSep )
		{
			break;
		}
		else if ( ch == htmlHexCharMarker )
		{
			ch = (hexToNibble( pPath[cur+1] ) << 4) + hexToNibble( pPath[cur+2] );
			newPath.concat( ch );
			cur += 2;
		}
		else if ( ch == htmlSep )
		{
			newPath.concat( dirSep );
		}
		else newPath.concat( ch );
		cur++;
	}
	pPath = newPath;
	return qtrue;
}


qbool WPHtmlClass::extractPath( str255& pPath )
{
	qshort p = pPath.pos( htmlBookmarkSep );
	if ( p ) pPath.deleet( p, 255 );
	
	qshort cur = pPath.length();
	while ( cur )
	{
		if ( pPath[cur] == htmlSep )
		{
			pPath[0] = (qchar)cur;
			return qtrue;
		}
		cur--;
	}
	return qfalse;
}


qbool WPHtmlClass::appendPath( str255& pPath, str255 pSubPath )
{
	str255 sub;
	if ( pPath[pPath.length()] == htmlSep ) pPath.deleet( pPath.length(), 1 );
	// Start rmmdocgen: if sub-path starts with "./" (meaning relative to the current directory)
	// then remove the ./ from the sub-path, ready to append to the current directory path
	if (1 == pSubPath.pos(htmlCurDir)) 
	{
		pSubPath.deleet(1, htmlCurDir.length());
		pPath.concat(htmlSep);
		pPath.concat(pSubPath);
		return qtrue;
	}
	else if (1 == pSubPath.pos(htmlUpOne))
	{
		// Handle links that use "../", in order to go up a level.
		// Each ../ encountered requires us to go up one level in pPath.
		int count = 0;
		do
		{
			pSubPath.deleet(1, htmlUpOne.length());
			extractPath(pPath);
			if (pPath[pPath.length()] == htmlSep) pPath.deleet(pPath.length(), 1);
		} while (1 == pSubPath.pos(htmlUpOne) && ++count < 100);	// Count prevents unexpected infinite loop
		pPath.concat(htmlSep);
		pPath.concat(pSubPath);
		return qtrue;
	}
	// End rmmdocgen
	while ( pSubPath.length() )
	{
		qshort p = pSubPath.pos( htmlSep );
		if ( p )
		{
			sub.copy( pSubPath, 1, p-1 );
			pSubPath.deleet( 1, p );
		}
		else
		{
			sub = pSubPath;
			pSubPath = qnil;
		}
		if ( sub == htmlBack )
		{
			qshort cur = pPath.length();
			while ( cur && pPath[cur] != htmlSep ) cur--;
			if ( cur )
			{
				pPath.deleet( cur, 255 );
			}
		}
		else
		{
			pPath.concat( htmlSep );
			pPath.concat( sub );
		}
	}
	return qtrue;
}

// Start rmmuni
void WPHtmlClass::init()
{
	sHtmlToUnicodeMapCfu = new CHRconvFromBytes((qbyte *) sHTMLchars, sHTMLcharsLen);
}

void WPHtmlClass::term()
{
	// Start rmmdocgen: clear pointers
	if (sHtmlToUnicodeMapCfu) { delete sHtmlToUnicodeMapCfu; sHtmlToUnicodeMapCfu = 0; }
	if (sHTMLtagCfu) { delete sHTMLtagCfu; sHTMLtagCfu = 0; }
	if (sHTMLtags) { delete [] sHTMLtags; sHTMLtags = 0; }
	// End rmmdocgen
}

// AE6075
void WPHtmlClass::getTitle( EXTfldval& pFval )
{
	if ( !mDocPtr )
		pFval.setChar((qchar*)QTEXT(""),0); 
	else
		pFval.setChar(&mDocPtr[mTitleStart],mTitleLen); 
} // mpm4700

// End rmmuni

// End of file
