/* $Header$ */
// rtf WP document object class definition

/**************** Changes ******************
Date			Edit				Bug					Description
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.
29-Oct-20	rmm10736		ST/EC/1627	Fixed infinite loop parsing various RTF documents.
31-Dec-18	rmm9881			ST/EC/1524	Issues with RTF on reports - text spanning pages had problems, and tabs did not always render nicely.
14-Sep-17	CR0472			ST/EC/1474	Fix crash when component is disconnected.
01-Jun-15	rmmhdpi									High DPI support for Windows.
04-Nov-14	rmm8453			ST/EC/1351	Crash on Linux.
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.
06-Dec-05	pkmach_o								pkmacho compiler changes - gStringTable now allocated and not a global static
28-OCT-05 AE6619			ST/EC/946 	Small tweak to rmm5254 (if undone then reverify this fault!!!!)
18-Apr-05	rmm5334			UN/IE/092		...continued: $importencoding and $exportencoding.
14-Apr-05	rmm5333			UN/IE/092		Unicode version import-export now supports different Ansi code pages.
17-Jan-05	rmm5254			UN/EC/883		Improved Unicode character support when viewing RTF in the Unicode version.
06-Jan-05 AE6556			ST/EC/879 	Allow documents with missing end of paragraph markers (poor, very poor)
06-Jan-05	AE6555			ST/EC/879 	Docview had problems with ud font tables
05-Mar-04	rmmuni_osx9							Mac OSX Unicode port.
26-Feb-04	rmmuni_osx7							Mac OSX Unicode port.
04 MAR 02 mpm5072			st/wo/1351	Implements non-breaking space character
20 NOV 01 mpm5023a								Now make copy of data prior to converting for returning to data field
19 NOV 01 mpm5023									Fixes umlaut problems
15 NOV 01 mpm_rmm4180							More RTF problems
01-Nov-01	rmm4177									More problems with files generated by RTF editor.
27-Dec-00	rmm3975			ST/EC/625		Problem with files created by RTF editor.
26 JUN 00 MHCARBON								OSX changes.
22 MAY 00	mpm4700									Implements printing of document
23-Mar-00	mlr0024			ST/EC/469		Sort out linux build
08 NOV 99 mlr0006									Initial version for Rtf support
********************************************/

/* this is a class to read an rtf document 
note that rtf works on tags after the event 
eg \cell after the text for  the cell hence
many objects are created as orphans & then 
given a parent later. 
*/

 // Start jmg_includecasing
#ifndef _extcomp_
#include "extcomp.he"
#endif

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

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

#ifndef	_WPTEXT_HE_
#include "wptext.he"
#endif

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

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

#ifndef	_WPXCOMP_HE_
#include "wpxcomp.he"
#endif

#ifndef	_WPRTF_HE_
#include "wprtf.he"
#endif
 // End jmg_includecasing
 
#ifndef _WPRTFUTL_HE_
#include "wprtfutl.he"
#endif

// rmm5334:
#ifndef _dmconst_
	#include "dmconst.he"
#endif


#include <math.h>
#include <string.h>
#include <stdlib.h>


// Start rmm5254: ANSI codepage to Unicode conversion tables
// rmm5333: moved standard ANSI codepage conversion tables to chrbasic.cpp
qushort gANSI10000[256] =	// MacRoman
{
	0x0000,	// 00: NULL
	0x0001,	// 01: START OF HEADING
	0x0002,	// 02: START OF TEXT
	0x0003,	// 03: END OF TEXT
	0x0004,	// 04: END OF TRANSMISSION
	0x0005,	// 05: ENQUIRY
	0x0006,	// 06: ACKNOWLEDGE
	0x0007,	// 07: BELL
	0x0008,	// 08: BACKSPACE
	0x0009,	// 09: HORIZONTAL TABULATION
	0x000A,	// 0A: LINE FEED
	0x000B,	// 0B: VERTICAL TABULATION
	0x000C,	// 0C: FORM FEED
	0x000D,	// 0D: CARRIAGE RETURN
	0x000E,	// 0E: SHIFT OUT
	0x000F,	// 0F: SHIFT IN
	0x0010,	// 10: DATA LINK ESCAPE
	0x0011,	// 11: DEVICE CONTROL ONE
	0x0012,	// 12: DEVICE CONTROL TWO
	0x0013,	// 13: DEVICE CONTROL THREE
	0x0014,	// 14: DEVICE CONTROL FOUR
	0x0015,	// 15: NEGATIVE ACKNOWLEDGE
	0x0016,	// 16: SYNCHRONOUS IDLE
	0x0017,	// 17: END OF TRANSMISSION BLOCK
	0x0018,	// 18: CANCEL
	0x0019,	// 19: END OF MEDIUM
	0x001A,	// 1A: SUBSTITUTE
	0x001B,	// 1B: ESCAPE
	0x001C,	// 1C: FILE SEPARATOR
	0x001D,	// 1D: GROUP SEPARATOR
	0x001E,	// 1E: RECORD SEPARATOR
	0x001F,	// 1F: UNIT SEPARATOR
	0x0020,	// 20: SPACE
	0x0021,	// 21: EXCLAMATION MARK
	0x0022,	// 22: QUOTATION MARK
	0x0023,	// 23: NUMBER SIGN
	0x0024,	// 24: DOLLAR SIGN
	0x0025,	// 25: PERCENT SIGN
	0x0026,	// 26: AMPERSAND
	0x0027,	// 27: APOSTROPHE
	0x0028,	// 28: LEFT PARENTHESIS
	0x0029,	// 29: RIGHT PARENTHESIS
	0x002A,	// 2A: ASTERISK
	0x002B,	// 2B: PLUS SIGN
	0x002C,	// 2C: COMMA
	0x002D,	// 2D: HYPHEN-MINUS
	0x002E,	// 2E: FULL STOP
	0x002F,	// 2F: SOLIDUS
	0x0030,	// 30: DIGIT ZERO
	0x0031,	// 31: DIGIT ONE
	0x0032,	// 32: DIGIT TWO
	0x0033,	// 33: DIGIT THREE
	0x0034,	// 34: DIGIT FOUR
	0x0035,	// 35: DIGIT FIVE
	0x0036,	// 36: DIGIT SIX
	0x0037,	// 37: DIGIT SEVEN
	0x0038,	// 38: DIGIT EIGHT
	0x0039,	// 39: DIGIT NINE
	0x003A,	// 3A: COLON
	0x003B,	// 3B: SEMICOLON
	0x003C,	// 3C: LESS-THAN SIGN
	0x003D,	// 3D: EQUALS SIGN
	0x003E,	// 3E: GREATER-THAN SIGN
	0x003F,	// 3F: QUESTION MARK
	0x0040,	// 40: COMMERCIAL AT
	0x0041,	// 41: LATIN CAPITAL LETTER A
	0x0042,	// 42: LATIN CAPITAL LETTER B
	0x0043,	// 43: LATIN CAPITAL LETTER C
	0x0044,	// 44: LATIN CAPITAL LETTER D
	0x0045,	// 45: LATIN CAPITAL LETTER E
	0x0046,	// 46: LATIN CAPITAL LETTER F
	0x0047,	// 47: LATIN CAPITAL LETTER G
	0x0048,	// 48: LATIN CAPITAL LETTER H
	0x0049,	// 49: LATIN CAPITAL LETTER I
	0x004A,	// 4A: LATIN CAPITAL LETTER J
	0x004B,	// 4B: LATIN CAPITAL LETTER K
	0x004C,	// 4C: LATIN CAPITAL LETTER L
	0x004D,	// 4D: LATIN CAPITAL LETTER M
	0x004E,	// 4E: LATIN CAPITAL LETTER N
	0x004F,	// 4F: LATIN CAPITAL LETTER O
	0x0050,	// 50: LATIN CAPITAL LETTER P
	0x0051,	// 51: LATIN CAPITAL LETTER Q
	0x0052,	// 52: LATIN CAPITAL LETTER R
	0x0053,	// 53: LATIN CAPITAL LETTER S
	0x0054,	// 54: LATIN CAPITAL LETTER T
	0x0055,	// 55: LATIN CAPITAL LETTER U
	0x0056,	// 56: LATIN CAPITAL LETTER V
	0x0057,	// 57: LATIN CAPITAL LETTER W
	0x0058,	// 58: LATIN CAPITAL LETTER X
	0x0059,	// 59: LATIN CAPITAL LETTER Y
	0x005A,	// 5A: LATIN CAPITAL LETTER Z
	0x005B,	// 5B: LEFT SQUARE BRACKET
	0x005C,	// 5C: REVERSE SOLIDUS
	0x005D,	// 5D: RIGHT SQUARE BRACKET
	0x005E,	// 5E: CIRCUMFLEX ACCENT
	0x005F,	// 5F: LOW LINE
	0x0060,	// 60: GRAVE ACCENT
	0x0061,	// 61: LATIN SMALL LETTER A
	0x0062,	// 62: LATIN SMALL LETTER B
	0x0063,	// 63: LATIN SMALL LETTER C
	0x0064,	// 64: LATIN SMALL LETTER D
	0x0065,	// 65: LATIN SMALL LETTER E
	0x0066,	// 66: LATIN SMALL LETTER F
	0x0067,	// 67: LATIN SMALL LETTER G
	0x0068,	// 68: LATIN SMALL LETTER H
	0x0069,	// 69: LATIN SMALL LETTER I
	0x006A,	// 6A: LATIN SMALL LETTER J
	0x006B,	// 6B: LATIN SMALL LETTER K
	0x006C,	// 6C: LATIN SMALL LETTER L
	0x006D,	// 6D: LATIN SMALL LETTER M
	0x006E,	// 6E: LATIN SMALL LETTER N
	0x006F,	// 6F: LATIN SMALL LETTER O
	0x0070,	// 70: LATIN SMALL LETTER P
	0x0071,	// 71: LATIN SMALL LETTER Q
	0x0072,	// 72: LATIN SMALL LETTER R
	0x0073,	// 73: LATIN SMALL LETTER S
	0x0074,	// 74: LATIN SMALL LETTER T
	0x0075,	// 75: LATIN SMALL LETTER U
	0x0076,	// 76: LATIN SMALL LETTER V
	0x0077,	// 77: LATIN SMALL LETTER W
	0x0078,	// 78: LATIN SMALL LETTER X
	0x0079,	// 79: LATIN SMALL LETTER Y
	0x007A,	// 7A: LATIN SMALL LETTER Z
	0x007B,	// 7B: LEFT CURLY BRACKET
	0x007C,	// 7C: VERTICAL LINE
	0x007D,	// 7D: RIGHT CURLY BRACKET
	0x007E,	// 7E: TILDE
	0x007F,	// 7F: DELETE
	0x00C4,	// 80: LATIN CAPITAL LETTER A WITH DIAERESIS
	0x00C5,	// 81: LATIN CAPITAL LETTER A WITH RING ABOVE
	0x00C7,	// 82: LATIN CAPITAL LETTER C WITH CEDILLA
	0x00C9,	// 83: LATIN CAPITAL LETTER E WITH ACUTE
	0x00D1,	// 84: LATIN CAPITAL LETTER N WITH TILDE
	0x00D6,	// 85: LATIN CAPITAL LETTER O WITH DIAERESIS
	0x00DC,	// 86: LATIN CAPITAL LETTER U WITH DIAERESIS
	0x00E1,	// 87: LATIN SMALL LETTER A WITH ACUTE
	0x00E0,	// 88: LATIN SMALL LETTER A WITH GRAVE
	0x00E2,	// 89: LATIN SMALL LETTER A WITH CIRCUMFLEX
	0x00E4,	// 8A: LATIN SMALL LETTER A WITH DIAERESIS
	0x00E3,	// 8B: LATIN SMALL LETTER A WITH TILDE
	0x00E5,	// 8C: LATIN SMALL LETTER A WITH RING ABOVE
	0x00E7,	// 8D: LATIN SMALL LETTER C WITH CEDILLA
	0x00E9,	// 8E: LATIN SMALL LETTER E WITH ACUTE
	0x00E8,	// 8F: LATIN SMALL LETTER E WITH GRAVE
	0x00EA,	// 90: LATIN SMALL LETTER E WITH CIRCUMFLEX
	0x00EB,	// 91: LATIN SMALL LETTER E WITH DIAERESIS
	0x00ED,	// 92: LATIN SMALL LETTER I WITH ACUTE
	0x00EC,	// 93: LATIN SMALL LETTER I WITH GRAVE
	0x00EE,	// 94: LATIN SMALL LETTER I WITH CIRCUMFLEX
	0x00EF,	// 95: LATIN SMALL LETTER I WITH DIAERESIS
	0x00F1,	// 96: LATIN SMALL LETTER N WITH TILDE
	0x00F3,	// 97: LATIN SMALL LETTER O WITH ACUTE
	0x00F2,	// 98: LATIN SMALL LETTER O WITH GRAVE
	0x00F4,	// 99: LATIN SMALL LETTER O WITH CIRCUMFLEX
	0x00F6,	// 9A: LATIN SMALL LETTER O WITH DIAERESIS
	0x00F5,	// 9B: LATIN SMALL LETTER O WITH TILDE
	0x00FA,	// 9C: LATIN SMALL LETTER U WITH ACUTE
	0x00F9,	// 9D: LATIN SMALL LETTER U WITH GRAVE
	0x00FB,	// 9E: LATIN SMALL LETTER U WITH CIRCUMFLEX
	0x00FC,	// 9F: LATIN SMALL LETTER U WITH DIAERESIS
	0x2020,	// A0: DAGGER
	0x00B0,	// A1: DEGREE SIGN
	0x00A2,	// A2: CENT SIGN
	0x00A3,	// A3: POUND SIGN
	0x00A7,	// A4: SECTION SIGN
	0x2022,	// A5: BULLET
	0x00B6,	// A6: PILCROW SIGN
	0x00DF,	// A7: LATIN SMALL LETTER SHARP S
	0x00AE,	// A8: REGISTERED SIGN
	0x00A9,	// A9: COPYRIGHT SIGN
	0x2122,	// AA: TRADE MARK SIGN
	0x00B4,	// AB: ACUTE ACCENT
	0x00A8,	// AC: DIAERESIS
	0x2260,	// AD: NOT EQUAL TO
	0x00C6,	// AE: LATIN CAPITAL LETTER AE
	0x00D8,	// AF: LATIN CAPITAL LETTER O WITH STROKE
	0x221E,	// B0: INFINITY
	0x00B1,	// B1: PLUS-MINUS SIGN
	0x2264,	// B2: LESS-THAN OR EQUAL TO
	0x2265,	// B3: GREATER-THAN OR EQUAL TO
	0x00A5,	// B4: YEN SIGN
	0x00B5,	// B5: MICRO SIGN
	0x2202,	// B6: PARTIAL DIFFERENTIAL
	0x2211,	// B7: N-ARY SUMMATION
	0x220F,	// B8: N-ARY PRODUCT
	0x03C0,	// B9: GREEK SMALL LETTER PI
	0x222B,	// BA: INTEGRAL
	0x00AA,	// BB: FEMININE ORDINAL INDICATOR
	0x00BA,	// BC: MASCULINE ORDINAL INDICATOR
	0x03A9,	// BD: GREEK CAPITAL LETTER OMEGA
	0x00E6,	// BE: LATIN SMALL LETTER AE
	0x00F8,	// BF: LATIN SMALL LETTER O WITH STROKE
	0x00BF,	// C0: INVERTED QUESTION MARK
	0x00A1,	// C1: INVERTED EXCLAMATION MARK
	0x00AC,	// C2: NOT SIGN
	0x221A,	// C3: SQUARE ROOT
	0x0192,	// C4: LATIN SMALL LETTER F WITH HOOK
	0x2248,	// C5: ALMOST EQUAL TO
	0x2206,	// C6: INCREMENT
	0x00AB,	// C7: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
	0x00BB,	// C8: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
	0x2026,	// C9: HORIZONTAL ELLIPSIS
	0x00A0,	// CA: NO-BREAK SPACE
	0x00C0,	// CB: LATIN CAPITAL LETTER A WITH GRAVE
	0x00C3,	// CC: LATIN CAPITAL LETTER A WITH TILDE
	0x00D5,	// CD: LATIN CAPITAL LETTER O WITH TILDE
	0x0152,	// CE: LATIN CAPITAL LIGATURE OE
	0x0153,	// CF: LATIN SMALL LIGATURE OE
	0x2013,	// D0: EN DASH
	0x2014,	// D1: EM DASH
	0x201C,	// D2: LEFT DOUBLE QUOTATION MARK
	0x201D,	// D3: RIGHT DOUBLE QUOTATION MARK
	0x2018,	// D4: LEFT SINGLE QUOTATION MARK
	0x2019,	// D5: RIGHT SINGLE QUOTATION MARK
	0x00F7,	// D6: DIVISION SIGN
	0x25CA,	// D7: LOZENGE
	0x00FF,	// D8: LATIN SMALL LETTER Y WITH DIAERESIS
	0x0178,	// D9: LATIN CAPITAL LETTER Y WITH DIAERESIS
	0x2044,	// DA: FRACTION SLASH
	0x20AC,	// DB: EURO SIGN
	0x2039,	// DC: SINGLE LEFT-POINTING ANGLE QUOTATION MARK
	0x203A,	// DD: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
	0xFB01,	// DE: LATIN SMALL LIGATURE FI
	0xFB02,	// DF: LATIN SMALL LIGATURE FL
	0x2021,	// E0: DOUBLE DAGGER
	0x00B7,	// E1: MIDDLE DOT
	0x201A,	// E2: SINGLE LOW-9 QUOTATION MARK
	0x201E,	// E3: DOUBLE LOW-9 QUOTATION MARK
	0x2030,	// E4: PER MILLE SIGN
	0x00C2,	// E5: LATIN CAPITAL LETTER A WITH CIRCUMFLEX
	0x00CA,	// E6: LATIN CAPITAL LETTER E WITH CIRCUMFLEX
	0x00C1,	// E7: LATIN CAPITAL LETTER A WITH ACUTE
	0x00CB,	// E8: LATIN CAPITAL LETTER E WITH DIAERESIS
	0x00C8,	// E9: LATIN CAPITAL LETTER E WITH GRAVE
	0x00CD,	// EA: LATIN CAPITAL LETTER I WITH ACUTE
	0x00CE,	// EB: LATIN CAPITAL LETTER I WITH CIRCUMFLEX
	0x00CF,	// EC: LATIN CAPITAL LETTER I WITH DIAERESIS
	0x00CC,	// ED: LATIN CAPITAL LETTER I WITH GRAVE
	0x00D3,	// EE: LATIN CAPITAL LETTER O WITH ACUTE
	0x00D4,	// EF: LATIN CAPITAL LETTER O WITH CIRCUMFLEX
	0xF8FF,	// F0: Apple logo
	0x00D2,	// F1: LATIN CAPITAL LETTER O WITH GRAVE
	0x00DA,	// F2: LATIN CAPITAL LETTER U WITH ACUTE
	0x00DB,	// F3: LATIN CAPITAL LETTER U WITH CIRCUMFLEX
	0x00D9,	// F4: LATIN CAPITAL LETTER U WITH GRAVE
	0x0131,	// F5: LATIN SMALL LETTER DOTLESS I
	0x02C6,	// F6: MODIFIER LETTER CIRCUMFLEX ACCENT
	0x02DC,	// F7: SMALL TILDE
	0x00AF,	// F8: MACRON
	0x02D8,	// F9: BREVE
	0x02D9,	// FA: DOT ABOVE
	0x02DA,	// FB: RING ABOVE
	0x00B8,	// FC: CEDILLA
	0x02DD,	// FD: DOUBLE ACUTE ACCENT
	0x02DB,	// FE: OGONEK
	0x02C7	// FF: CARON
};

#ifdef ismac
	// Special handling for symbol charset on the Mac
	qushort gANSISymbol[256] =	// Symbol
	{
		0x0000,	// 00: NULL
		0x0001,	// 01: START OF HEADING
		0x0002,	// 02: START OF TEXT
		0x0003,	// 03: END OF TEXT
		0x0004,	// 04: END OF TRANSMISSION
		0x0005,	// 05: ENQUIRY
		0x0006,	// 06: ACKNOWLEDGE
		0x0007,	// 07: BELL
		0x0008,	// 08: BACKSPACE
		0x0009,	// 09: HORIZONTAL TABULATION
		0x000A,	// 0A: LINE FEED
		0x000B,	// 0B: VERTICAL TABULATION
		0x000C,	// 0C: FORM FEED
		0x000D,	// 0D: CARRIAGE RETURN
		0x000E,	// 0E: SHIFT OUT
		0x000F,	// 0F: SHIFT IN
		0x0010,	// 10: DATA LINK ESCAPE
		0x0011,	// 11: DEVICE CONTROL ONE
		0x0012,	// 12: DEVICE CONTROL TWO
		0x0013,	// 13: DEVICE CONTROL THREE
		0x0014,	// 14: DEVICE CONTROL FOUR
		0x0015,	// 15: NEGATIVE ACKNOWLEDGE
		0x0016,	// 16: SYNCHRONOUS IDLE
		0x0017,	// 17: END OF TRANSMISSION BLOCK
		0x0018,	// 18: CANCEL
		0x0019,	// 19: END OF MEDIUM
		0x001A,	// 1A: SUBSTITUTE
		0x001B,	// 1B: ESCAPE
		0x001C,	// 1C: FILE SEPARATOR
		0x001D,	// 1D: GROUP SEPARATOR
		0x001E,	// 1E: RECORD SEPARATOR
		0x001F,	// 1F: UNIT SEPARATOR
		0x0020,	// 20: SPACE
		0x0021,	// 21: EXCLAMATION MARK
		0x2200,	// 22: FOR ALL
		0x0023,	// 23: NUMBER SIGN
		0x2203,	// 24: THERE EXISTS
		0x0025,	// 25: PERCENT SIGN
		0x0026,	// 26: AMPERSAND
		0x220D,	// 27: SMALL CONTAINS AS MEMBER
		0x0028,	// 28: LEFT PARENTHESIS
		0x0029,	// 29: RIGHT PARENTHESIS
		0x2217,	// 2A: ASTERISK OPERATOR
		0x002B,	// 2B: PLUS SIGN
		0x002C,	// 2C: COMMA
		0x2212,	// 2D: MINUS SIGN
		0x002E,	// 2E: FULL STOP
		0x002F,	// 2F: SOLIDUS
		0x0030,	// 30: DIGIT ZERO
		0x0031,	// 31: DIGIT ONE
		0x0032,	// 32: DIGIT TWO
		0x0033,	// 33: DIGIT THREE
		0x0034,	// 34: DIGIT FOUR
		0x0035,	// 35: DIGIT FIVE
		0x0036,	// 36: DIGIT SIX
		0x0037,	// 37: DIGIT SEVEN
		0x0038,	// 38: DIGIT EIGHT
		0x0039,	// 39: DIGIT NINE
		0x003A,	// 3A: COLON
		0x003B,	// 3B: SEMICOLON
		0x003C,	// 3C: LESS-THAN SIGN
		0x003D,	// 3D: EQUALS SIGN
		0x003E,	// 3E: GREATER-THAN SIGN
		0x003F,	// 3F: QUESTION MARK
		0x2245,	// 40: APPROXIMATELY EQUAL TO
		0x0391,	// 41: GREEK CAPITAL LETTER ALPHA
		0x0392,	// 42: GREEK CAPITAL LETTER BETA
		0x03A7,	// 43: GREEK CAPITAL LETTER CHI
		0x0394,	// 44: GREEK CAPITAL LETTER DELTA
		0x0395,	// 45: GREEK CAPITAL LETTER EPSILON
		0x03A6,	// 46: GREEK CAPITAL LETTER PHI
		0x0393,	// 47: GREEK CAPITAL LETTER GAMMA
		0x0397,	// 48: GREEK CAPITAL LETTER ETA
		0x0399,	// 49: GREEK CAPITAL LETTER IOTA
		0x03D1,	// 4A: GREEK THETA SYMBOL
		0x039A,	// 4B: GREEK CAPITAL LETTER KAPPA
		0x039B,	// 4C: GREEK CAPITAL LETTER LAMDA
		0x039C,	// 4D: GREEK CAPITAL LETTER MU
		0x039D,	// 4E: GREEK CAPITAL LETTER NU
		0x039F,	// 4F: GREEK CAPITAL LETTER OMICRON
		0x03A0,	// 50: GREEK CAPITAL LETTER PI
		0x0398,	// 51: GREEK CAPITAL LETTER THETA
		0x03A1,	// 52: GREEK CAPITAL LETTER RHO
		0x03A3,	// 53: GREEK CAPITAL LETTER SIGMA
		0x03A4,	// 54: GREEK CAPITAL LETTER TAU
		0x03A5,	// 55: GREEK CAPITAL LETTER UPSILON
		0x03C2,	// 56: GREEK SMALL LETTER FINAL SIGMA
		0x03A9,	// 57: GREEK CAPITAL LETTER OMEGA
		0x039E,	// 58: GREEK CAPITAL LETTER XI
		0x03A8,	// 59: GREEK CAPITAL LETTER PSI
		0x0396,	// 5A: GREEK CAPITAL LETTER ZETA
		0x005B,	// 5B: LEFT SQUARE BRACKET
		0x2234,	// 5C: THEREFORE
		0x005D,	// 5D: RIGHT SQUARE BRACKET
		0x22A5,	// 5E: UP TACK
		0x005F,	// 5F: LOW LINE
		0xF8E5,	// 60: radical extender # corporate char
		0x03B1,	// 61: GREEK SMALL LETTER ALPHA
		0x03B2,	// 62: GREEK SMALL LETTER BETA
		0x03C7,	// 63: GREEK SMALL LETTER CHI
		0x03B4,	// 64: GREEK SMALL LETTER DELTA
		0x03B5,	// 65: GREEK SMALL LETTER EPSILON
		0x03C6,	// 66: GREEK SMALL LETTER PHI
		0x03B3,	// 67: GREEK SMALL LETTER GAMMA
		0x03B7,	// 68: GREEK SMALL LETTER ETA
		0x03B9,	// 69: GREEK SMALL LETTER IOTA
		0x03D5,	// 6A: GREEK PHI SYMBOL
		0x03BA,	// 6B: GREEK SMALL LETTER KAPPA
		0x03BB,	// 6C: GREEK SMALL LETTER LAMDA
		0x03BC,	// 6D: GREEK SMALL LETTER MU
		0x03BD,	// 6E: GREEK SMALL LETTER NU
		0x03BF,	// 6F: GREEK SMALL LETTER OMICRON
		0x03C0,	// 70: GREEK SMALL LETTER PI
		0x03B8,	// 71: GREEK SMALL LETTER THETA
		0x03C1,	// 72: GREEK SMALL LETTER RHO
		0x03C3,	// 73: GREEK SMALL LETTER SIGMA
		0x03C4,	// 74: GREEK SMALL LETTER TAU
		0x03C5,	// 75: GREEK SMALL LETTER UPSILON
		0x03D6,	// 76: GREEK PI SYMBOL
		0x03C9,	// 77: GREEK SMALL LETTER OMEGA
		0x03BE,	// 78: GREEK SMALL LETTER XI
		0x03C8,	// 79: GREEK SMALL LETTER PSI
		0x03B6,	// 7A: GREEK SMALL LETTER ZETA
		0x007B,	// 7B: LEFT CURLY BRACKET
		0x007C,	// 7C: VERTICAL LINE
		0x007D,	// 7D: RIGHT CURLY BRACKET
		0x223C,	// 7E: TILDE OPERATOR
		0x00BF,	// 7F: no mapping (character not present in ansi code page) - map to inverted question mark
		0x00BF,	// 80: no mapping
		0x00BF,	// 81: no mapping
		0x00BF,	// 82: no mapping
		0x00BF,	// 83: no mapping
		0x00BF,	// 84: no mapping
		0x00BF,	// 85: no mapping
		0x00BF,	// 86: no mapping
		0x00BF,	// 87: no mapping
		0x00BF,	// 88: no mapping
		0x00BF,	// 89: no mapping
		0x00BF,	// 8A: no mapping
		0x00BF,	// 8B: no mapping
		0x00BF,	// 8C: no mapping
		0x00BF,	// 8D: no mapping
		0x00BF,	// 8E: no mapping
		0x00BF,	// 8F: no mapping
		0x00BF,	// 80: no mapping
		0x00BF,	// 91: no mapping
		0x00BF,	// 92: no mapping
		0x00BF,	// 93: no mapping
		0x00BF,	// 94: no mapping
		0x00BF,	// 95: no mapping
		0x00BF,	// 96: no mapping
		0x00BF,	// 97: no mapping
		0x00BF,	// 98: no mapping
		0x00BF,	// 99: no mapping
		0x00BF,	// 9A: no mapping
		0x00BF,	// 9B: no mapping
		0x00BF,	// 9C: no mapping
		0x00BF,	// 9D: no mapping
		0x00BF,	// 9E: no mapping
		0x00BF,	// 9F: no mapping
		0x20AC,	// A0: EURO SIGN
		0x03D2,	// A1: GREEK UPSILON WITH HOOK SYMBOL
		0x2032,	// A2: PRIME	# minute
		0x2264,	// A3: LESS-THAN OR EQUAL TO
		0x2044,	// A4: FRACTION SLASH
		0x221E,	// A5: INFINITY
		0x0192,	// A6: LATIN SMALL LETTER F WITH HOOK
		0x2663,	// A7: BLACK CLUB SUIT
		0x2666,	// A8: BLACK DIAMOND SUIT
		0x2665,	// A9: BLACK HEART SUIT
		0x2660,	// AA: BLACK SPADE SUIT
		0x2194,	// AB: LEFT RIGHT ARROW
		0x2190,	// AC: LEFTWARDS ARROW
		0x2191,	// AD: UPWARDS ARROW
		0x2192,	// AE: RIGHTWARDS ARROW
		0x2193,	// AF: DOWNWARDS ARROW
		0x00B0,	// B0: DEGREE SIGN
		0x00B1,	// B1: PLUS-MINUS SIGN
		0x2033,	// B2: DOUBLE PRIME	# second
		0x2265,	// B3: GREATER-THAN OR EQUAL TO
		0x00D7,	// B4: MULTIPLICATION SIGN
		0x221D,	// B5: PROPORTIONAL TO
		0x2202,	// B6: PARTIAL DIFFERENTIAL
		0x2022,	// B7: BULLET
		0x00F7,	// B8: DIVISION SIGN
		0x2260,	// B9: NOT EQUAL TO
		0x2261,	// BA: IDENTICAL TO
		0x2248,	// BB: ALMOST EQUAL TO
		0x2026,	// BC: HORIZONTAL ELLIPSIS
		0x23D0,	// BD: VERTICAL LINE EXTENSION (for arrows) # for Unicode 4.0 and later
		0x23AF,	// BE: HORIZONTAL LINE EXTENSION (for arrows) # for Unicode 3.2 and later
		0x21B5,	// BF: DOWNWARDS ARROW WITH CORNER LEFTWARDS
		0x2135,	// C0: ALEF SYMBOL
		0x2111,	// C1: BLACK-LETTER CAPITAL I
		0x211C,	// C2: BLACK-LETTER CAPITAL R
		0x2118,	// C3: SCRIPT CAPITAL P
		0x2297,	// C4: CIRCLED TIMES
		0x2295,	// C5: CIRCLED PLUS
		0x2205,	// C6: EMPTY SET
		0x2229,	// C7: INTERSECTION
		0x222A,	// C8: UNION
		0x2283,	// C9: SUPERSET OF
		0x2287,	// CA: SUPERSET OF OR EQUAL TO
		0x2284,	// CB: NOT A SUBSET OF
		0x2282,	// CC: SUBSET OF
		0x2286,	// CD: SUBSET OF OR EQUAL TO
		0x2208,	// CE: ELEMENT OF
		0x2209,	// CF: NOT AN ELEMENT OF
		0x2220,	// D0: ANGLE
		0x2207,	// D1: NABLA
		0x00AE,	// D2: REGISTERED SIGN # serif
		0x00A9,	// D3: COPYRIGHT SIGN # serif
		0x2122,	// D4: TRADE MARK SIGN # serif
		0x220F,	// D5: N-ARY PRODUCT
		0x221A,	// D6: SQUARE ROOT
		0x22C5,	// D7: DOT OPERATOR
		0x00AC,	// D8: NOT SIGN
		0x2227,	// D9: LOGICAL AND
		0x2228,	// DA: LOGICAL OR
		0x21D4,	// DB: LEFT RIGHT DOUBLE ARROW
		0x21D0,	// DC: LEFTWARDS DOUBLE ARROW
		0x21D1,	// DD: UPWARDS DOUBLE ARROW
		0x21D2,	// DE: RIGHTWARDS DOUBLE ARROW
		0x21D3,	// DF: DOWNWARDS DOUBLE ARROW
		0x22C4,	// E0: DIAMOND OPERATOR
		0x3008,	// E1: LEFT ANGLE BRACKET
		0x00AE,	// E2:+F87F	; REGISTERED SIGN, alternate: sans serif
		0x00A9,	// E3:+F87F	; COPYRIGHT SIGN, alternate: sans serif
		0x2122,	// E4:+F87F	; TRADE MARK SIGN, alternate: sans serif
		0x2211,	// E5: N-ARY SUMMATION
		0x239B,	// E6: LEFT PARENTHESIS UPPER HOOK # for Unicode 3.2 and later
		0x239C,	// E7: LEFT PARENTHESIS EXTENSION # for Unicode 3.2 and later
		0x239D,	// E8: LEFT PARENTHESIS LOWER HOOK # for Unicode 3.2 and later
		0x23A1,	// E9: LEFT SQUARE BRACKET UPPER CORNER # for Unicode 3.2 and later
		0x23A2,	// EA: LEFT SQUARE BRACKET EXTENSION # for Unicode 3.2 and later
		0x23A3,	// EB: LEFT SQUARE BRACKET LOWER CORNER # for Unicode 3.2 and later
		0x23A7,	// EC: LEFT CURLY BRACKET UPPER HOOK # for Unicode 3.2 and later
		0x23A8,	// ED: LEFT CURLY BRACKET MIDDLE PIECE # for Unicode 3.2 and later
		0x23A9,	// EE: LEFT CURLY BRACKET LOWER HOOK # for Unicode 3.2 and later
		0x23AA,	// EF: CURLY BRACKET EXTENSION # for Unicode 3.2 and later
		0xF8FF,	// F0: Apple logo
		0x3009,	// F1: RIGHT ANGLE BRACKET
		0x222B,	// F2: INTEGRAL
		0x2320,	// F3: TOP HALF INTEGRAL
		0x23AE,	// F4: INTEGRAL EXTENSION # for Unicode 3.2 and later
		0x2321,	// F5: BOTTOM HALF INTEGRAL
		0x239E,	// F6: RIGHT PARENTHESIS UPPER HOOK # for Unicode 3.2 and later
		0x239F,	// F7: RIGHT PARENTHESIS EXTENSION # for Unicode 3.2 and later
		0x23A0,	// F8: RIGHT PARENTHESIS LOWER HOOK # for Unicode 3.2 and later
		0x23A4,	// F9: RIGHT SQUARE BRACKET UPPER CORNER # for Unicode 3.2 and later
		0x23A5,	// FA: RIGHT SQUARE BRACKET EXTENSION # for Unicode 3.2 and later
		0x23A6,	// FB: RIGHT SQUARE BRACKET LOWER CORNER # for Unicode 3.2 and later
		0x23AB,	// FC: RIGHT CURLY BRACKET UPPER HOOK # for Unicode 3.2 and later
		0x23AC,	// FD: RIGHT CURLY BRACKET MIDDLE PIECE # for Unicode 3.2 and later
		0x23AD,	// FE: RIGHT CURLY BRACKET LOWER HOOK # for Unicode 3.2 and later
		0x00BF	// FF: no mapping
	};
	#endif	// #ifdef ismac
// End rmm5254

// rmm5254: removed sSysToUnicode

// IMPORTANT NOTE: Please use spaces to pad the words (DO NOT USE TABS)
// This is sized to listoverridetable the current max.
// 
// rmm5254: new string
static const char* sRtfSTRING1 = "\
'                \
*                \
ansi             \
ansicpg          \
";

// rmm5254: split and renumbered
static const char* sRtfSTRING2 = "\
author           \
b                \
bin              \
blue             \
brdrcf           \
brdrw            \
bullet           \
cb               \
cell             \
cellx            \
cf               \
clcbpat          \
clmgf            \
clmrg            \
clvertalb        \
clvertalc        \
clvertalt        \
clvmgf           \
clvmrg           \
colortbl         \
cs               \
deff             \
emdash           \
endash           \
expnd            \
expndtw          \
f                \
fcharset         \
field            \
filetbl          \
fmodern          \
fonttbl          \
footer           \
footerf          \
footerl          \
footerr          \
footnote         \
froman           \
fs               \
fswiss           \
ftech            \
green            \
header           \
headerf          \
headerl          \
headerr          \
hyphcaps         \
hyphhotz         \
i                \
info             \
intbl            \
jpegblip         \
ldblquote        \
leveljc          \
levelnfc         \
levelstartat     \
li               \
line             \
list             \
listid           \
listlevel        \
listname         \
listoverridetable\
listsimple       \
liststarthdn     \
listtable        \
lquote           \
";
 
// rmm5254: renumbered
static const char* sRtfSTRING3 = "\
ls               \
mac              \
marglsxn         \
margtsxn         \
nonshppict       \
nosupersub       \
objclass         \
objdata          \
object           \
operator         \
paperh           \
paperw           \
par              \
pard             \
pichgoal         \
pict             \
picwgoal         \
plain            \
pngblip          \
pntext           \
qc               \
qj               \
ql               \
qr               \
rdblquote        \
red              \
revtbl           \
row              \
rquote           \
rtf              \
rxe              \
s                \
sa               \
sb               \
sect             \
sectd            \
shppict          \
strike           \
stylesheet       \
sub              \
super            \
tab              \
tc               \
title            \
trgaph           \
trowd            \
trqc             \
trql             \
trqr             \
trrh             \
txe              \
u                \
uc               \
ud               \
ul               \
uld              \
uldb             \
ulnone           \
ulw              \
upr              \
vertalb          \
vertalc          \
vertalj          \
vertalt          \
xe               \
";


enum eTagID {
eTagQuote=0,eTagStar,
eTagAnsi,eTagAnsiCpg,	// rmm5254
eTagAuthor,eTagB,eTagBin,eTagBlue,eTagBrdrcf,eTagBrdrw,eTagBullet,
eTagCb,eTagCell,eTagCellx,eTagCf,eTagClcbpat,
eTagClmgf,eTagClmrg,eTagClvertalb,eTagClvertalc,eTagClvertalt,
eTagClvmgf,eTagClvmrg,eTagColortbl,eTagCs,eTagDeff,
eTagEmdash,eTagEndash,eTagExpnd,eTagExpndtw,eTagF,eTagFcharset,	// rmm5254: added eTagFcharset
eTagField,eTagFiletbl,eTagFmodern,eTagFonttbl,eTagFooter,
eTagFooterf,eTagFooterl,eTagFooterr,eTagFootnote,eTagFroman,eTagFs,
eTagFswiss,eTagFtech,eTagGreen,eTagHeader,eTagHeaderf,eTagHeaderl,
eTagHeaderr,eTagHyphcaps,eTagHyphhotz,eTagI,eTagInfo,eTagIntbl,
eTagJpegBlip,
eTagLdblquote,eTagLevelJc,eTagLevelnfc,eTagLevelstartat,
eTagLi,eTagLine,eTagList, eTagListId,eTagListLevel,eTagListname,
eTagListoverridetable,eTagListSimple,eTagListstarthdn,eTagListtable,
eTagLquote,eTagLs,eTagMac,eTagMarglsxn,eTagMargtsxn,eTagNonShppict,
eTagNoSupersub,eTagObjClass,eTagObjData,
eTagObject,eTagOperator,eTagPaperh,eTagPaperw,eTagPar,eTagPard,eTagPichgoal,eTagPict,
eTagPicwgoal,eTagPlain,eTagPngBlip,
eTagPntext,eTagQc,eTagQj,eTagQl,eTagQr,eTagRdblquote,eTagRed,eTagRevtbl,
eTagRow,eTagRquote,eTagRtf,eTagRxe,eTagS,eTagSa,eTagSb,
eTagSect,eTagSectd,eTagShppict,eTagStrike,eTagStylesheet,eTagSub,eTagSuper,
eTagTab,eTagTc,eTagTitle,eTagTrgaph,eTagTrowd,eTagTrqc,eTagTrql,eTagTrqr,
eTagTrrh,eTagTxe,eTagU,eTagUc,eTagUd,eTagUl,eTagUld,eTagUldb,eTagUlnone,eTagUlw,
eTagUpr,eTagVertalb,
eTagVertalc,eTagVertalj,eTagVertalt,eTagXe,eTagHexa,eTagHexd,eTagOpenBrace,
eTagCloseBrace,eTagCharacter,eTagSemiColon,eTagSpace,
eTagHex,eTagSpecial,eTagUnknown
};


// the tagIds are defined with the strings for ease of maintainence 
// hence these must be hear not in class definition.

static	eTagID	sTagID;									// current tag ID - set by nextRTFtag
static	eTagID	sPreviousTag;						// last tagId used for /* syntax

str31		sTag;														// current tag name - set by nextHTMLtag
static	qbool		sNumberSet;							// sais if this tag has a number
static	qlong		sNumber;								// Current numer attached to tag
static	qlong		sNoOpenGroups;
static	qbool		sInList;
static	qlong		sThisBulletNumStyle ;
static	qbool		sTagHaveBreak;
static	qbool		sFinishedHeading ;
static	qshort	sDefaultFontNo = -1; // rmm5254

static qlong sMaxUnicodeNo ;
static qreal sSceenRes ;

void sSetMaxUnicodeNo(qlong pLenQchar)	// rmmuni
{
	if (pLenQchar >= 4) sMaxUnicodeNo = 0x7fffffff;
	else sMaxUnicodeNo = (qlong)pow((double) 256,(double) pLenQchar) ;
}


// mpm5023 begins
#define RTF_OPEN_GROUP_CHAR '{' 
#define RTF_CLOSE_GROUP_CHAR '}'
#define RTF_OPEN_TAG_CHAR  '\\' 
// mpm5023 ends

#define RTF_HYPHEN '-' 
#define RTF_OPEN_GROUP 0x02		// mpm5023
#define RTF_CLOSE_GROUP 0x03	// mpm5023
#define RTF_OPEN_TAG  0x01		// mpm5023
#define RTF_STRIP_THIS_CHARACTER 0x04	// rmm5254
#define RTF_SEMICOLON ';'
#define RTF_CR_HEXD 0x0d
#define RTF_LF_HEXA 0x0a

// copied from wphtml.cpp
eTagID sConvRtfTag( strxxx& pName );
void createTempFileName(str255 &tempFile);

WPRtfClass::WPRtfClass( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting ) // mpm4700
						:WPDocClass( pCrlHwnd, pEventHwnd, pIdent, pPrinting )
{

	mColorTable=NULL;
	mFontTab = NULL ;
	mListTab = NULL ; 
	mStyleParaTab = NULL ;
	mStyleTextTab = NULL ;
	mFilesCreated = NULL ;
	// rmm5254:
	mAnsiCodePageTable = 0;

	sTagID = eTagUnknown;
	sPreviousTag = eTagUnknown ;
	sNoOpenGroups = 0;
	sFinishedHeading = qfalse ;
	sTagHaveBreak=qfalse ;
	sNumberSet = qfalse;
	sNumber = 0;
	sInList = qfalse;
	sThisBulletNumStyle = 0 ;
	sDefaultFontNo = 0 ;

	// need to be able to convert twips, a twentieth of a point ,
	// a point = 1/72 inch, to pixels 
	qdim sHorz;
	qdim sVert;
	// have got GDIgetScreenResolution use the average val.
	GDIgetScreenResolution(&sHorz,&sVert );
	sHorz = GDIconvToDesign(sHorz, qfalse);	// rmmhdpi
	sVert = GDIconvToDesign(sVert, qtrue);	// rmmhdpi
	sSceenRes = qreal((sHorz + sVert) / 2) / qreal(20 * 72);

	mParagraphInfo.mSpaceAfter = mParagraphInfo.mSpaceBefore = 0;	// rmm4177
}

WPRtfClass::~WPRtfClass()
{
// need to kill mColorTable etc
	if ( mColorTable != NULL )
		delete mColorTable;
	mColorTable = NULL;
	if ( mFontTab != NULL )
		delete mFontTab;
	mFontTab = NULL;
	if ( mListTab != NULL )
		delete mListTab;
	mListTab = NULL;
	if ( mStyleParaTab != NULL )
		delete mStyleParaTab;
	mStyleParaTab = NULL;
	if ( mStyleTextTab != NULL )
		delete mStyleTextTab;
	if ( mFilesCreated != NULL)
		delete mFilesCreated;
	mFilesCreated = NULL;
}


WPRtfClass* WPRtfClass::make( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting, // mpm4700
 															WPDocInfoStruct* pDocInfo, str255& pDocument, qchar* pSrchWords )
{
	WPRtfClass* obj = new WPRtfClass( pCrlHwnd, pEventHwnd, pIdent, pPrinting ); // mpm4700
	if ( obj )
	{
		if ( pDocInfo ) 
			obj->mDocInfo = *pDocInfo;
		else
			obj->mDocInfo.init( eWPdocRTF );
	}
	obj->parseDocument( pDocument, pSrchWords );
	return obj;
}


qbool WPRtfClass::nextRTFtag( qlong& pInd, qbool countBraces, qbool pWillParseRtfText)
{
	sPreviousTag = sTagID;
	sTagID = eTagUnknown ;
	sNumberSet = qfalse ; 
	qchar c;

	while ( pInd < (qlong)mDocPtr.charLen() )
	{
		c = mDocPtr[pInd] ;	
		switch ( c )
		{
			case RTF_OPEN_GROUP :
				sTagID =  eTagOpenBrace;
				if (countBraces)
					sNoOpenGroups++;
				pInd++;
				return qtrue;
			case RTF_CLOSE_GROUP :
				sTagID =  eTagCloseBrace;
				if (countBraces)
					sNoOpenGroups--;
				pInd++;
				return qtrue;
			case RTF_SEMICOLON :
				sTagID = eTagSemiColon ;
				pInd++;
				return qtrue;
			case RTF_OPEN_TAG :
			{
				pInd++;
				qlong start = pInd;
				c = mDocPtr[pInd];
				if (( !wpRtfIsalpha(c)) && (c != '*') )
				// i.e a control symbol with no delimeter 
				{
					if (c == RTF_CLOSE_GROUP || c == RTF_OPEN_GROUP || c == RTF_OPEN_TAG || c == RTF_CR_HEXD || c == RTF_LF_HEXA) // rmm5254: added CR and LF
					{
						// Start rmm5254
						// this is an escaped char; this is now handled in parseRtfText, so decrement the index so that it addresses the
						// RTF_OPEN_TAG
						if (pWillParseRtfText) --pInd;
						// End rmm5254
						sTagID = eTagCharacter;
						return qtrue;
					}
					/* mpm5023 if ( mDocPtr[pInd] == '\'' ) // this is a hex val 
					{
						sTagID = eTagHex  ;
						pInd++;
						tempCharForDebug = 		mDocPtr[pInd] ;	
						return qtrue;
					}*/
					// this includes |,~,-_: 
					sTag[0] = 1 ; // length   
					sTag[1] = mDocPtr[pInd++] ;
					sTag[2] = '\0' ;
					sTagID = eTagSpecial ;	// rmm5254
					return qtrue;						// rmm5254
				}
				while ( pInd < (qlong)mDocPtr.charLen() )
				{
					c = mDocPtr[pInd];
					if (( c == RTF_CR_HEXD) 
						|| (c == RTF_LF_HEXA ))
					{
						mDocPtr[pInd] = ' ';
						c = ' ';
					}

					if ( c == ' '	 || (c == RTF_OPEN_TAG )
						|| (c == RTF_OPEN_GROUP )|| (c == RTF_CLOSE_GROUP )
						|| c == RTF_HYPHEN			 ||  c ==  RTF_SEMICOLON  ||
						 wpRtfIsdigit (c)  )
					{
						sTag = str31( (qshort)(pInd - start), &mDocPtr[start] );

						// it would be better to covert to lower as all rest is lower
						// but cant see fn - sTag.upps(); would do to upper

						// now deal with number
						sNumber = getDigit(pInd);
						sTagID = sConvRtfTag( sTag );
						// Start rmm5254: the space is part of the control word (see RTF spec)
						// Test mDocPtr[pInd], in case getDigit was used to increase pInd
						if (mDocPtr[pInd] == ' ')
							++pInd;
						// Next, skip any CR's and LF's, as some documents have a control word with its space terminator followed by CRLF
						while (pInd < (qlong) mDocPtr.charLen())
						{
							c = mDocPtr[pInd];
							if (RTF_CR_HEXD == c || RTF_LF_HEXA == c)
								++pInd;
							else
								break;
						}
						// End rmm5254
						return qtrue;
					} // found end of open tag 
					pInd++;
				} // found end of file without closing tag 
				if ( pInd > start )
				{
					// at end of doc but one tag left .
					sTag = str31( (qshort)(pInd - start), &mDocPtr[start] );
					sNumber = getDigit(pInd);
					sTagID = sConvRtfTag( sTag );
					return qtrue;
				} 

			} // end of open tag
			// rmm5254: case ' ' : 
			case RTF_CR_HEXD : case RTF_LF_HEXA :
				sTagID =  eTagSpace;
				// this overwrites cr & lf a space.
				mDocPtr[pInd] = ' ';
				pInd++ ;
				return qtrue;
			default : // assume char 
			{		
				// note if this is not dealt with by calling fn loops.
				sTagID = eTagCharacter;
				if (' ' == c && !pWillParseRtfText) pInd++;	// rmm10736
				return qtrue;
			}

		}// end of switch on mDocPtr[pInd]
	} // end of while not end of data 
	return qfalse; // i.e no further tag found 
} // end of nextRTFtag

// This does a binary chop of tags 
// need to make sure 17 is max len  
// assumes need 2 sets of strings then copy into one area 
// of memory  as too long for one

static qlong sRTFtagsMaxWordLen = 17;
static CHRconvFromBytes *sRTFtagsCfu = 0;

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

static qchar convHexNibble(qchar n) // mpm5023 begins
{
	if ( n>='a' && n<='f')
		return n-'a'+10;
	else if ( n>='0' && n<='9')
		return n-'0';
	else if ( n>='A' && n<='F')
		return n-'A'+10;
	else
		return 0;
}

void WPRtfClass::convDocument()
{
	qlong oldSize = QCHARLEN(HANglobalSize(mDocHan));
	qlong newSize = 0;
	qHandle newHan = HANglobalAlloc( QBYTELEN(oldSize) );
	qHandleTextPtr newPtr( newHan, 0 );
	qchar* oldChar = &mDocPtr[0];
	qchar* newChar = &newPtr[0];
	while ( oldSize )
	{
		qchar ch = *oldChar;
		oldSize--; oldChar++;
		if ( ch == RTF_OPEN_TAG_CHAR )
		{
			ch = RTF_OPEN_TAG;
			switch ( *oldChar )
			{
				case '\'':
					{ // we have got a hex char, convert it
						oldSize--; oldChar++;
						ch = convHexNibble(*oldChar) << 4;
						oldSize--; oldChar++;
						ch += convHexNibble(*oldChar);
						oldSize--; oldChar++;
						break;
					}
				case '~': // mpm5072 begins
					{ // we have a non-breaking space, convert it
						ch = fixspace; // fixspace declared in xgdi.he
						oldSize--; oldChar++;
						break;
					} // mpm5072 ends
				case RTF_OPEN_TAG_CHAR:
				case RTF_OPEN_GROUP_CHAR:
				case RTF_CLOSE_GROUP_CHAR:
					{
						ch = *oldChar;
						oldSize--; oldChar++;
						break;
					}
				default:
					{ // make sure we copy next character as is
						*newChar = RTF_OPEN_TAG;
						newSize++; newChar++;
						ch = *oldChar;
						oldSize--; oldChar++;
						break;
					}
			}
		}
		else if ( ch == RTF_OPEN_GROUP_CHAR )
		{
			ch = RTF_OPEN_GROUP;
		}
		else if ( ch == RTF_CLOSE_GROUP_CHAR )
		{
			ch = RTF_CLOSE_GROUP;
		}

		*newChar = ch;
		newSize++; newChar++;
	}
	if ( mOrigDocHan ) HANglobalFree( mOrigDocHan ); // mpm5023a
	mOrigDocHan = mDocHan; // mpm5023a
	newPtr.dataLen( QBYTELEN(newSize) );
	mDocHan = newHan;
	mDocPtr = newPtr;

} // mpm5023 ends


void WPRtfClass::parseDocument( str255& pDocument, qchar* pSrchWords )
{
	str255 document = pDocument;
	WPHtmlClass::htmlToApiPath( document );
	// note html version does some name messing Does rtf neeed that ?
	if ( !sRTFtagsCfu ) initTags(); // rmmuni
	
	EXTfile file; 
	qret fileOpened = file.open( document, qtrue, qfalse );
	if ( fileOpened == e_ok )
	{
		mDocHan = 0;
		// Do not convert 8 bit characters when we read the file - they will be
		// mapped directly to qchars without conversion, since the RTF data contains character set information.
		qret fileRead1 = file.readCharacterData(mDocHan, EXTfile::eFILEnoConversion);
		if ( fileRead1 == e_ok && !!mDocHan) // rmm8453
		{
			mDocPtr = qHandlePtr( mDocHan, 0 );
			qlong index = 0; 
			mStackPtr = 0; 
			mDocURL = pDocument;
			mTextInfo.mStyle = WP_DOCINFO_BASESTYLE;
			mTextInfo.mSet = WPTEXT_STYLESET;
			mTextInfo.mFontSize = 10;
			mTextInfo.mFontSizeMode = eWPFontSizePoints;
			mTextInfo.mSet |= WPTEXT_FONTSIZESET;
				
			convDocument(); // mpm5023
			// If want to do serch for words do it here.
			// but difficult as may be split by tags & contain jpeg
			// or binary data.

			sSetMaxUnicodeNo(sizeof(mDocPtr[index]));

			while ( nextRTFtag(index) )
			{
				ProcessRtfTag( index);
			}// end of while nextRTFtag
		} // end of file read ok 
		file.close();
	} // end of fileOpened ok 

} // end of parseDocument

// startring from rtf text string 
WPRtfClass* WPRtfClass::make( HWND pCrlHwnd, HWND pEventHwnd, qlong pIdent, qbool pPrinting, // mpm4700
 															WPDocInfoStruct* pDocInfo,EXTfldval *fldval)
{
	WPRtfClass* obj = new WPRtfClass( pCrlHwnd, pEventHwnd, pIdent, pPrinting ); // mpm4700
	if ( obj )
	{
		if ( pDocInfo ) 
			obj->mDocInfo = *pDocInfo;
		else
			obj->mDocInfo.init( eWPdocRTF );
	}
	obj->parseText( fldval);
	return obj;
}
// not sure what format to imput this probely need the Handler fns 
// Try with str255 !
void WPRtfClass::parseText(EXTfldval *fldval)
{
	mDocHan = fldval->getHandle( qtrue );
	mDocPtr = qHandleTextPtr( mDocHan, 0 );
	// this doesnt compile on mac : is  it required
	// mDocPtr.dataLen( mDocHan->mDataLen());
	if ( mDocPtr.charLen() > 0 )
	{
		convDocument(); // mpm5023
		if ( !sRTFtagsCfu ) initTags(); // rmmuni
		mStackPtr = 0; 
		qlong index = 0; 
		mDocURL = QTEXT("");
		mTextInfo.mStyle = WP_DOCINFO_BASESTYLE;
		mTextInfo.mSet = WPTEXT_STYLESET;
		sSetMaxUnicodeNo(sizeof(mDocPtr[index]));
		while ( nextRTFtag(index) )
		{
			ProcessRtfTag( index);
		}// end of while nextRTFtag
	}
}
void WPRtfClass::ProcessRtfTag( qlong& pInd)
{

	// work out if heading or main prog 
	switch (sTagID )
	{
		case eTagRtf :
		{
			// all docs should start {/rtf
			processMainDoc(pInd);
			break;
		}
		case eTagOpenBrace :
		{
			// expected
			break;
		}
		default :
		{
			processMainDoc( pInd );
			// unexpected needs trace
		}
	}
} // rtf is the only flag you expect at begining 

void WPRtfClass::parseRtfInfo(qlong& pInd )
{
// this is called after /info is found  & gives the title .
	qlong infoOpenLevels = sNoOpenGroups;
	qbool foundUprTag = qfalse;	// rmmuni
	while (nextRTFtag(pInd))
	{			
		switch (sTagID)
		{
			case eTagTitle :
			{
				mTitle = readText(pInd, qtrue, foundUprTag);	// rmmuni: use return value
				// this is in html what does it do 
				if ( !mPrinting ) // mpm4700
				{
					EXTfldval fvals[2];
					fvals[0].setLong( mIdent );
					fvals[1].setChar( mTitle );
					ECOsendEvent( mEventHwnd, cWPsetTitleEvent, &fvals[0], 2, qtrue );
				}
				
				// have title dont want anything else so
				while ( sNoOpenGroups >= infoOpenLevels )
					ignoreGroup(pInd); // this gets out of title & info
				return;
			} // end of title
			// what if no title need to exit when noLevels < 1
			case eTagOpenBrace : case eTagSpace :
			{
				break;
			}
			case eTagCloseBrace :
			{
				if ( sNoOpenGroups < infoOpenLevels )
					return ;
			}
			case eTagCharacter : // not sure if this is correct but 
			// if dont catch end up spinning 
			{
				skipToBraceOrSpace(pInd);
				break;
			} 
			case eTagUpr :	// rmmuni
			{
				foundUprTag = qtrue;
				ignoreNonUnicode(pInd);
				break;
			}
		}// end of switch 
	}
	return ;
}
	
qbool WPRtfClass::processHeaderDefinitions(qlong& pInd)
{
	qbool tagFound = qfalse ;
	switch (sTagID )
	{
		case eTagFonttbl :
		{
			// from this  we want font number & description. 
			getFontTable(pInd);
			setDefaultFont();	// rmm5254
			tagFound=qtrue;
			break;
		}
		case eTagFiletbl : // file table ignore this.
			skipToBraceOrSpace(pInd);
			tagFound=qtrue;
			break;
		case eTagColortbl: // color table 
			readColorTab(pInd);	
			tagFound=qtrue;
			break;
		case eTagStylesheet: // need to sort out para vs txt styles
			getStyleSheet(pInd);
			tagFound=qtrue;
			break;
		case	eTagListtable:  // Bullet point definition
			getListTable (pInd);
			tagFound=qtrue;
			break;
		case eTagListoverridetable : // id nos for Bullet points
			getListOverRideTable (pInd);
			tagFound=qtrue;
			break;
		case eTagInfo : // This contains the title
		{
			parseRtfInfo(pInd);
			tagFound=qtrue;
			sFinishedHeading=qtrue;
			break;
		}
		// dont need default as handled in calling fn				
	}// end of switch
	return tagFound ;
}

// Start rmm5254
void WPRtfClass::setDefaultFont(qbool pNeedFont, qbool pNeedFontSize)
{
	// Set up the default font
	if (pNeedFont && sDefaultFontNo >= 0 && mFontTab)
	{
		qushort *ansiCodePageTable = 0;
		mTextInfo.mFontNum = mFontTab->findFont(sDefaultFontNo, &ansiCodePageTable);
		mTextInfo.mSet |= WPTEXT_CODEPAGESET;
		mAnsiCodePageTable = ansiCodePageTable;
		mTextInfo.mSet |= WPTEXT_FONTNUMSET;
	}
	if (pNeedFontSize) mTextInfo.mFontSize = 12;
}

EXTqlist *sWinFontList = 0;
EXTqlist *sRepFontList = 0;	
static qbool sLookupFont(strxxx &pFontString, EXTqlist *pFontList)
{
	qlong lineCount = pFontList->rowCnt();
	EXTfldval fval;
	for (qlong lineNumber = 1; lineNumber <= lineCount; ++lineNumber)
	{
		pFontList->getColValRef(lineNumber, 1, fval, qfalse);
		if (!fval.compare(pFontString, qtrue))
		{
			return qtrue;
		}
	}
	return qfalse;
}
// End rmm5254

void WPRtfClass::getFontTable(qlong& pInd )
{
	qshort	rtfFontNumber ;						// font no from rtf
	qshort	charset = 0;							// rmm5254: zero means default to Ansi
	str31		fontString ;
	pushParaSettings();
	pushTextSettings();
	qbool firstFont = qtrue;
	qbool noFace= qtrue;
	mFontTab = new wpRtfFontTab();
	wpRtfFontTab * fontTab = mFontTab ;
	qlong fontTableStartNoGroupsOpen = sNoOpenGroups ;

	while ( nextRTFtag(pInd) )
	{			
		switch (sTagID )
		{
			case  eTagF : // fontNumber
			{
				rtfFontNumber= (qshort)sNumber ;
				noFace = qtrue ;
				break;
			}
			case eTagFcharset:	// rmm5254
			{
				charset = (qshort) sNumber;
				break;
			}
			case eTagCharacter : 
				// This should be a textString containing the face 
				fontString = readText(pInd,qfalse);
				noFace = qfalse;
				break;
			case eTagFroman :
			{
				if ( noFace )
					fontString = QTEXT("Times New Roman");
				break;
			}
			case eTagFswiss :
			{
				if ( noFace )
					fontString = QTEXT("Arial");
				break;
			}
			case eTagFmodern :
			{
				if ( noFace )
					fontString = QTEXT("Courier New");
				break;
			}
			case eTagFtech :
			{
				if ( noFace)
					fontString = QTEXT("Symbol");
				break;
			}
			case eTagOpenBrace :
			{
					break;
			}
			case eTagCloseBrace :
			{
				if (sNoOpenGroups < 	fontTableStartNoGroupsOpen )
				{
					popParaSettings();
					popTextSettings();
					return;
				}
				break;
			}
			case eTagSemiColon :
			{
				// end of group create font 
				// Start rmm5254: validate font name - if necessary, change the name to a font which exists
				if (!sWinFontList)
				{
					sWinFontList = new EXTqlist(listVlen);
					ECOlistFonts(sWinFontList, qfalse);

					sRepFontList = new EXTqlist(listVlen);
					ECOlistFonts(sRepFontList, qtrue);
				}
				// Look up the font in the lists - we use both, as sometimes the font only exists in the report font list, but still works OK.
				if (!sLookupFont(fontString, sWinFontList) && !sLookupFont(fontString, sRepFontList))
				{
					// Strip "-" and any following characters: Mac RTF sometimes includes extra stuff after the font name
					qshort dashPos = fontString.pos('-');
					if (dashPos)
					{
						fontString[0] = dashPos - 1;
						if (!sLookupFont(fontString, sWinFontList) && !sLookupFont(fontString, sRepFontList))
						{
							// Use a default
							#if defined(ismacosx)
								fontString = QTEXT("Lucida Grande");
							#elif defined(ismac)
								fontString = QTEXT("Geneva");
							#elif defined(iswin32)
								// Use this for Win32 and Unix - Unix font mapping via omnisxi.so should convert to a Unix font
								fontString = QTEXT("Tahoma");
							#endif
						}
					}
				}
				// End rmm5254
				qfnt gdiFontNo;
				GDIsetFontName( &gdiFontNo, &fontString[1], fontString.length() );
				str255 face2; 
				// Note [0] = len then [1] = str copied from wphtml.cpp.

				face2[0] = (qchar)GDIgetFontName( &gdiFontNo, &face2[1], 255 );
				qshort  fontNo = GDIgetFontNumber( &gdiFontNo );
										
				if (firstFont )
				{
					fontTab->setFont(rtfFontNumber,fontNo,charset); // rmm5254
					firstFont= qfalse ;
				}
				else
					fontTab = fontTab->addFont(rtfFontNumber,fontNo,charset); // rmm5254
				rtfFontNumber = 0 ;          // font no from rtf
				fontString = QTEXT("") ;
				noFace= qtrue;
				charset = 0;	// rmm5254
				break;
					// save font ;
			}
			case eTagUnknown:
			{
				if (( sPreviousTag == eTagStar ) 
				|| 		(sPreviousTag == eTagOpenBrace ) )
				{
					ignoreGroup(pInd);
					pInd-- ; // this sets p = { so returns from this group
					// need to counter the effect of going back one
					sNoOpenGroups ++ ;
				}
				break;
			}
			default :
			{
			// unexpected ignore
			}
		} // end switch 
	} // end while
}

void WPRtfClass::getListTable(qlong& pInd )
{
	// rmm5254: this code doesn't really do the right thing, but make what's here work better and 
	// also maintain compatibility with previous versions.
	mListTab = new wpRtfListTab();
	wpRtfListTab * listTab = mListTab ;
	eWPbullet	bullet = eWPbulletNone;
	qbool firstList = qtrue ;
	qlong listId = 0 ;
	qshort start =0 ;
	qlong allign =0;
	qshort indent=0;
	qshort groupsOpenForListTable = 0;	// rmm5254
	qlong listTableStartNoGroupsOpen = sNoOpenGroups ;	// AE6619

	while (nextRTFtag(pInd))
	{			
		switch (sTagID)
		{
			case  eTagListId : // Number into list override tab
			{
				listId = sNumber ;
				break;
			}
			case eTagLevelstartat :
			{
				start =( qshort)sNumber ;
				break;
			}
			case eTagLevelnfc :
			{
				bullet = wpRtfListTab::transRtfLevToBull(sNumber);
				break;
			}	
			case eTagLevelJc :
			{
				break;
			}
			case eTagListLevel :
			case eTagListSimple : // no of levels in list
			case eTagListstarthdn :
			case eTagListname :
			// in this case its the levels that are separated by 
			// semicolons not the list items.
			case eTagSemiColon :
			break;
			case eTagCharacter :
			{
				// not sure what this is but need to extract it 
				str31 listname = readText(pInd,qfalse);
        GDIignore(&listname);
				break;
			}
			case eTagOpenBrace :
			{
				++groupsOpenForListTable; // rmm5254
				break;
			}
			case eTagCloseBrace :
			{
				--groupsOpenForListTable;			// rmm5254
				if ( sNoOpenGroups < listTableStartNoGroupsOpen ) // AE6619 (brought old code back)!groupsOpenForListTable)	// rmm5254
				{
					if ( listId != 0 )
					{ 
						if (firstList )
						{
							listTab->setList( listId,bullet,allign,indent,start );
							firstList = qfalse ;
						}
						else
							listTab = listTab->addList(listId,bullet,allign,indent,start );
					}	
					return;
				}
				break;
			}
			case eTagList :
			{
				// end of group create bullet list item 
				if ( listId != 0 )
				{ 
					if (firstList )
					{
						listTab->setList( listId,bullet,allign,indent,start );
						firstList = qfalse ;
					}
					else
					listTab = listTab->addList(listId,bullet,allign,indent,start );
				}
				listId = 0 ;          // id to use list overide tab 
				start = 0 ;
				bullet = eWPbulletNone; 
				allign = 0;
				indent = 0 ;
				break;
			}
			case eTagLi: // line indent
			{
				if ( sNumberSet == qtrue)
					indent =  convTwipsToPixel(sNumber) ;
				else
					indent =  0 ;
				break;
			}
			case eTagUnknown:
			{
				// rmm5254: just ignore the tag, so brace counting for groupsOpenForListTable works OK
				break;
			}
			default :
			{
			// now what 
			}
		} // end switch 
	} // end while
}

void WPRtfClass::getListOverRideTable(qlong& pInd )
{
	// rmm5254: this code doesn't really do the right thing, but make what's here work better and 
	// also maintain compatibility with previous versions.
	wpRtfListTab *currentList = NULL ;
	if (mListTab == NULL)
	{ // have nowhere to attach this so ignore group.
		ignoreGroup(pInd);
		return;
	}
	qshort groupsOpenForListOverrideTable = 0;	// rmm5254

	while ( nextRTFtag(pInd) )
	{			
		switch (sTagID )
		{
			case  eTagListId :
				currentList = mListTab->findListId( sNumber);
				break;
			case eTagLs :
				if ( ( currentList != NULL ) && ( sNumberSet == qtrue ))
				{
					currentList->setListNo(sNumber);
					currentList = NULL; 
				}				
				break;
			case eTagOpenBrace :
			{
				++groupsOpenForListOverrideTable;	// rmm5254
				break;
			}
			case eTagCloseBrace :
			{
				--groupsOpenForListOverrideTable;				// rmm5254
				if (groupsOpenForListOverrideTable < 0)	// rmm5254
					return;
				break;
			}
			case eTagCharacter :
			{
				// not sure what this is 
				str31 listOverideName = readText(pInd,qfalse);
        GDIignore(&listOverideName);
				break;
			}
			default :
			{
				// now what 
			}
		}
	}
} // end of getListOverRideTable
// at this point you are in the first braces & have seen rtf style
	// Note in tables work backwards . trowd sais in table
	// but dont have the alignments & spacing until later
	// so create table before create cell. If in a table
	// need to add para to cell and cell to table. but get 
	// first text then posibly para then cell. So often have 
	// temporary varibles added to document later. 

void WPRtfClass::processMainDoc(qlong& pInd )
{
	// here after \rtf do everything could sort out header from info .
	// {\rtf.... \deff{}{}
	
	WPParagraphClass* para = NULL ;
	WPObjectClass* text = NULL; // mainly txt but may include picture
	qlong mainNoGroupsOpen = sNoOpenGroups ;
	
	// This tries to keep track of if to restart numbers 
	// in bulllet points.
	qshort lastBulletNo=0;
	qlong lastBulletNumStyle = 0 ;

	qbool inTable = qfalse ;
	WPTableClass* table = NULL; 
	WPTableInfoStruct tableInfo;
	tableInfo.mWidth = 1;
	tableInfo.mHAlign = eWPHAleft ;
	qbool tableStartNewColWidth = qtrue;
	cellInfoStack cellInfoStart;
	qldim prevCellWidth = 0 ;
	cellInfoStack *currentCell = &cellInfoStart ;
	qbool StartNewRow = qtrue ;
	qshort pardCounter = 0;
	while ( nextRTFtag(pInd, qtrue, qtrue) ) // rmm5254
	{			
		switch (sTagID )
		{
			case eTagOpenBrace :
			{
				pushTextSettings();
				break;
			}
			case eTagCloseBrace :
			{		
				popTextSettings();
				if (sNoOpenGroups < 	mainNoGroupsOpen )
				{
					if (pardCounter) endPara(lastBulletNo,lastBulletNumStyle,inTable,text,para,table); // rmm5254
					return ;
				}
				break;
			}		
			case eTagEndash : case eTagEmdash : case eTagRquote : 
			case eTagLquote : case eTagLdblquote :case eTagRdblquote : 
			case eTagTab : case eTagBullet :
			{
				// We have text but am not sure who will be the parent 
				if (text)
				{
					WPTextClass*  text2 = addSpecialChar(pInd,NULL);
					if ( text2 != NULL )
						text->orphanSetNext(text2);
				}
				else
					text = addSpecialChar(pInd,NULL);
				break;
			}
			case eTagCharacter :case eTagHex : case eTagU :
			{
				if ( text != NULL )
				{
					WPTextClass*  text2 = parseRtftext(pInd,NULL);
					if ( text2 != NULL )
						text->orphanSetNext(text2);
				}
				else 
				{
					text = parseRtftext(pInd,NULL);
				}
				break;
			}
			case eTagPar :
			{
				pardCounter--;	 // AE6556
				endPara(lastBulletNo,lastBulletNumStyle,inTable,text,para,table);
				break;
			}
			case eTagPard :
			{
				pardCounter++;	 // AE6556
				resetDefaultParaProperties();
				break;
			}
			case eTagTrowd :
			{
				// This causes a new table to be created possibly per row
				// but gets round the auto cell spacing in the table component.
				// Ok in an Ideal world the table would be created here
				// but the table info comes in after .
				tableStartNewColWidth = qtrue;
				table = NULL;
				StartNewRow = qtrue ;
				inTable = qtrue;
				break;
			}
			case 	eTagCellx : // this is the end of cell deinitions
			{
				if ( inTable )
				{
					if ( tableStartNewColWidth )
					{
						cellInfoStart.clear();
						prevCellWidth = 0 ;
						tableStartNewColWidth = qfalse ;
					}
					mCellInfo.mWidth  = convTwipsToPixel(sNumber )
						- prevCellWidth ;
					prevCellWidth = prevCellWidth + mCellInfo.mWidth;
					cellInfoStart.add(mCellInfo);

					// reset cell info 
					mCellInfo.mBackColor = tableInfo.mBackColor;
					mCellInfo.mHAlign = tableInfo.mHAlign  ;
					mCellInfo.mVAlign = tableInfo.mVAlign  ;
					mCellInfo.mBorderColor = tableInfo.mBorderColor;
					mCellInfo.mBorderSize =tableInfo .mBorderSize;
					break;
				}
			}
			case eTagCell :
			{
				if (inTable) 
				{
					WPTableCellInfoStruct *currCellInfo = 0;
					if ( currentCell != NULL )
					{
						currentCell = currentCell->nextCellInfo();
						if ( currentCell != NULL ) 
							currCellInfo =currentCell->cellInfo();
					}
					else 
						currCellInfo = &mCellInfo ;
					currCellInfo->mColSpan = mCellInfo.mColSpan ;
					//currCellInfo->mNoWrap = qtrue ;
					if (!table)
						table = WPTableClass::make( this, this, &tableInfo );
					WPObjectClass* cell = table->addCell( currCellInfo, StartNewRow );
					// problem know text already  but need to add to this cellPara
					if ( para != NULL )
					{
						addParaToCell(para,cell);
						para = NULL ;
					}
					if ( text != NULL )
					{	
						if  (sInList )
						{
							// note cant use mBullet as not well defined
							if ( lastBulletNumStyle == sThisBulletNumStyle )
							{ 
								mParagraphInfo.mNumber = ++lastBulletNo;
							}
							else
							{
								lastBulletNumStyle = sThisBulletNumStyle ;
								lastBulletNo = mParagraphInfo.mNumber;
							}
						}
						para = WPParagraphClass::make( cell, this, &mParagraphInfo );
						addTxtToPara(text,para);
						sInList =qfalse;
						text = NULL;
						para=NULL;
					}
					cell = NULL ;
					para=NULL;
					StartNewRow = qfalse ;
				}
			break;
			}
			case eTagIntbl :
			{
				inTable = qtrue ;
				break;
			}
			case eTagRow : 
			{
				if (inTable)
				{
					inTable = qfalse ;
					StartNewRow = qtrue ;
					tableStartNewColWidth = qtrue ;
					currentCell = &cellInfoStart ;
				}
				break;
			}
			case eTagPict : case eTagShppict : 
			{
				WPImageClass* pict = readPictData(pInd);
				if (pict != NULL)
				{
					if ( text != NULL )
					{
						text->orphanSetNext(pict);
					}
					else 
					{
						text = pict ;
					}
				}
				break;
			}
			case eTagUnknown :
			{
				if ( sPreviousTag == eTagStar )  
				{
					ignoreGroup(pInd);
					pInd-- ; // this sets p = { so returns from this group
					// need to counter the effect of going back one
					sNoOpenGroups ++ ;
				}
				break;
			}
			case eTagSpace :
			break;
			case eTagAnsi :
			{
				mFileFormat = eTagAnsi ;
				break;
			}
			case eTagMac :
			{
				mFileFormat = eTagMac ;
				break;
			}
			// Start rmm5254
			case eTagAnsiCpg:
			{
				// rmm5334
				switch (sNumber)
				{
					case 874:		mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiThai);						break;	// Start rmm5333: use CHRconvFromAnsiCodePage::getCodePage
					case 1250:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiCentralEuropean); break;
					case 1251:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiCyrillic);				break;
					case 1252:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiLatin1);					break;
					case 1253:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiGreek);						break;
					case 1254:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiTurkish);					break;
					case 1255:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiHebrew);					break;
					case 1256:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiArabic);					break;
					case 1257:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiBaltic);					break;
					case 1258:	mAnsiCodePageTable = CHRconvFromAnsiCodePage::getCodePage(preUniTypeAnsiVietnamese);			break;	// End rmm5333
					case 10000:	mAnsiCodePageTable = gANSI10000; break;
				}
				break;
			}
			// End rmm5254
			case eTagDeff :
			{
				if (sNumberSet == qtrue)
					sDefaultFontNo = (qshort)sNumber ;
				// rmm5254: need to carry on processing tags properly: skipToBraceOrSpace(pInd);
				break;
			} 
									
			case  eTagPaperh : case eTagPaperw :
			{
				// know about but are not interested ignore until space 
				// or {}
				// rmm5254: need to carry on processing tags properly: skipToBraceOrSpace(pInd);
				break;
			}
			case eTagAuthor: case eTagOperator:	// rmmuni
			{
				skipToCloseBrace(pInd);
				break;
			}
			default :
			{
				qbool found =qfalse ;
				if ( inTable )
					found = parseRtfTableTags(tableInfo);
				else if (sFinishedHeading == qfalse) // rmm5254: && ( sNoOpenGroups <=2 ))	// rmmuni: <=2, rather than == 2
					found = processHeaderDefinitions(pInd);
				if (!found)
					found = parseGeneralTags(pInd);
			}

		} // end of switch on tagId
	}
	if (pardCounter) endPara(lastBulletNo,lastBulletNumStyle,inTable,text,para,table); // AE6556
	return ;
}


qbool WPRtfClass::ignoreGroup(qlong& pInd )
{
	qbool tagOpen = qfalse ;
	qbool bChar = qfalse;

	qlong noOpenGroups = 1;
	while (( pInd < (qlong)mDocPtr.charLen() ) && ( noOpenGroups > 0 ))
	{
		// could check for /bin here as this will confuse
		qchar c = mDocPtr[pInd];
		if ( c == RTF_OPEN_TAG )
		  tagOpen = qtrue;
		else if (  tagOpen == qtrue && pInd == 'b')
			bChar = qtrue ;
		else
		{
			 tagOpen = qfalse ;
				bChar = qfalse;
		}

		if ( c == RTF_OPEN_GROUP )
			noOpenGroups++ ;
		else if ( c == RTF_CLOSE_GROUP )
			noOpenGroups--;

		if ( bChar == qtrue )
		{
			if ((pInd+1 == 'i' )&& (pInd+2 == 'n'))
			{
				if ( (c == RTF_HYPHEN  ) || wpRtfIsdigit(c) )
				{
					pInd = pInd+3 ;
					// now should be length ?i.e \bin234 
					readBinData(pInd);
				}
				// if not  ? 
			}
			tagOpen = qfalse ;
			bChar = qfalse;
		}
		pInd++;
	}
	sNoOpenGroups -- ;
	return qtrue;
}

qbool WPRtfClass::parseGeneralTags(qlong& pInd)
{
	qbool foundFlag = qtrue ;
	switch (sTagID )
	{		
		case eTagOpenBrace :
		{
			pushTextSettings();
			break;
		}
		case eTagCloseBrace :
		{
			popTextSettings();
			break;
		}
		case eTagUnknown:
		{
			if ( sPreviousTag == eTagStar ) 
			{
				ignoreGroup(pInd);
				pInd-- ; // this sets p = { so returns from this group
				// need to counter the effect of going back one
				sNoOpenGroups ++ ;
			}
			break;
		}
		case eTagSpace : // just a divider
		{
			break ;
		}
		case eTagCs :
		{
			setCharStyle(sNumber);
			break;
		}
		case eTagS :
		{
			setParaStyle(sNumber);
			break;
		}
		case eTagSectd :
		{
			// If can have more than one set of properies.				
			mDocInfo.mLeftMargin = 5;
			mDocInfo.mTopMargin  = 5;
			break;
			// if reset section properties should the paragrah 
			// properyies be reset
		}
		case eTagMarglsxn : // section left margin
		{
			mDocInfo.mLeftMargin= convTwipsToPixel(sNumber) ;
			break;
		}
		case eTagMargtsxn:  // section top margin 
		{
			mDocInfo.mTopMargin = convTwipsToPixel(sNumber) ; 
			break;
		}
		case eTagQl:
		{
			mParagraphInfo.mHAlign = eWPHAleft ;
			break;
		}
		case eTagQc:
		{
			// not sure where I get the paragraphInfo from
			mParagraphInfo.mHAlign = eWPHAcenter ;
			break;
		}
		case eTagQj:
		{
			mParagraphInfo.mHAlign = eWPHAjustify ;
			break;
		}
		case eTagQr:
		{
			mParagraphInfo.mHAlign = eWPHAright ;
			break;
		}
		case eTagLi: // line indent
		{
			if ( sNumberSet == qtrue)
				mParagraphInfo.mIndent =  convTwipsToPixel(sNumber) ;
			else
				mParagraphInfo.mIndent =  0 ;
			break;
		}
		case eTagSa : // space after
		{
			// not sure if need to convert this
			if ( sNumberSet == qtrue)
				mParagraphInfo.mSpaceAfter =  convTwipsToPixel(sNumber)/4 ;
			else
				mParagraphInfo.mSpaceAfter = 0   ;
			break;
		}
		case eTagSb :
		{
			// not sure why /2 empirical 
			if ( sNumberSet == qtrue)
				mParagraphInfo.mSpaceBefore =  convTwipsToPixel(sNumber)/4;
			else
				mParagraphInfo.mSpaceBefore =  0 ;
			break;
		}			
		case eTagVertalt:	case eTagVertalc: case eTagVertalb: case eTagVertalj :
		{
			// sets doc alignment cant do so ignore 
			// mVAlign = eWPVAtop,eWPVAmiddle,eWPVAbottom;
			break;
		}
		case eTagLine :
		{
			sTagHaveBreak = qtrue ;
 			break;
		}
		case eTagEndash : case eTagEmdash : case eTagRquote : 
		case eTagLquote : case eTagLdblquote :case eTagRdblquote : 
		case eTagTab : case eTagBullet :
		{
			// a text object should be treated a bit like char 
			// is delt with in calling fn.
			// only disgaurded in heading fns .
			break;
		}
		case eTagSpecial :
		{
			// need to sort this 			
			break;	
		}
		case eTagB : // bold text 
		{
			// note 0 is different to not set
			if ( sNumberSet ==  qfalse )
			{
				mTextInfo.mFontStyBold = qtrue ;
				mTextInfo.mSet |= WPTEXT_FONTSTYBSET;
			}
			else 
			{
				mTextInfo.mFontStyBold = qfalse ;
				mTextInfo.mSet |= WPTEXT_FONTSTYBSET;
			}
			break;
		}
		case eTagI : // italic text
		{
			if ( sNumberSet ==  qfalse )
			{
				mTextInfo.mFontStyItalic = qtrue;
				mTextInfo.mSet |= WPTEXT_FONTSTYISET;
			}
			else 
			{
				mTextInfo.mFontStyItalic = qfalse ;
				mTextInfo.mSet |= WPTEXT_FONTSTYISET;
			}
			break;
		}
		case eTagSub : // subscript
		{
			mTextInfo.mFontSub = qtrue;
			mTextInfo.mSet |= WPTEXT_FONTSUBSET;
			break;
		}
		case eTagNoSupersub : // no super or subscript
		{
			mTextInfo.mFontSub = qfalse;
			mTextInfo.mFontSuper = qfalse ;
			mTextInfo.mSet |= WPTEXT_FONTSUBSET | WPTEXT_FONTSUPERSET ;
			break;
		}
		case eTagSuper : // supperscript
		{
			mTextInfo.mFontSuper = qtrue;
			mTextInfo.mSet |= WPTEXT_FONTSUPERSET;
			break;
		}
				// note these should be different sorts of underlining .
		case eTagUl : case eTagUlw : case eTagUld : case eTagUldb :
		{
			mTextInfo.mSet |= WPTEXT_FONTSTYUSET;
			if ( sNumberSet ==  qfalse )
			{
				mTextInfo.mFontStyUnderline = qtrue;
			}
			else 
				mTextInfo.mFontStyUnderline = qfalse ;
			break;
		}
		case eTagUlnone :
		{
			mTextInfo.mSet |= WPTEXT_FONTSTYUSET;
			mTextInfo.mFontStyUnderline = qfalse ;
			break;
		}
		case eTagStrike :
		{
			mTextInfo.mSet |= WPTEXT_FONTSTRIKESET;
			if ( sNumberSet ==  qfalse )
			{
				mTextInfo.mFontStrike = qtrue;
			}
			else
				mTextInfo.mFontStrike = qfalse ;
			break;
		}
		case eTagFs : // font size in 1/2 points
		{
			if ( sNumberSet ==  qtrue )
			{	
				mTextInfo.mFontSize = (qshort) (sNumber / 2);
				mTextInfo.mFontSizeMode = eWPFontSizePoints;
				mTextInfo.mSet |= WPTEXT_FONTSIZESET | WPTEXT_FONTSIZEMODE;
			}
			else
			{
				mTextInfo.mFontSize = 24 / 2 ;
				mTextInfo.mFontSizeMode = eWPFontSizePoints;
				mTextInfo.mSet |= WPTEXT_FONTSIZESET | WPTEXT_FONTSIZEMODE;
			}
			break;
		}
		case eTagCf :
		{
			mTextInfo.mTextColor = getRtfColor();
			mTextInfo.mSet |= WPTEXT_TEXTCOLORSET;
			break;
		}
		case eTagCb :
		{
			mDocInfo.mBackColor = getRtfColor();
			break;
		}
		case eTagF : // font number 
		{
			if (mFontTab != NULL)
			{
				qushort *ansiCodePageTable = 0;	// rmm5254
				if ( sNumberSet == qtrue)
					mTextInfo.mFontNum = mFontTab->findFont((qshort)sNumber, &ansiCodePageTable);		// rmm5254
				else
					mTextInfo.mFontNum = mFontTab->findFont(sDefaultFontNo, &ansiCodePageTable);		// rmm5254
				mTextInfo.mSet |= WPTEXT_CODEPAGESET;
				mAnsiCodePageTable = ansiCodePageTable;
				mTextInfo.mSet |= WPTEXT_FONTNUMSET;
				if ( !mTextInfo.mFontSize ) mTextInfo.mFontSize = 12; // mpm_rmm4180
			}
			break;
		}
		case eTagPlain :
		{
			// rmm5254: mTextInfo.mSet = 0;
			mTextInfo.mSet &= ~(WPTEXT_FONTSTYBSET | WPTEXT_FONTSTYUSET | WPTEXT_FONTSTYISET);
			break;
		}
		case eTagLs: // listitem
		{
			if ( mListTab != NULL )
			{
				wpRtfListTab *tempList = mListTab->findList(sNumber);
				sInList = qtrue;
				sThisBulletNumStyle = sNumber ;
				eWPbullet tempBullet= eWPbulletNone ;
				qshort startNo = 1 ;
				if ( tempList != NULL )
				{
					tempBullet= tempList->getBullet();
					startNo = tempList->getStartLev();
					mParagraphInfo.mIndent = tempList->getIndent() ;
				}
				if (( tempBullet != mParagraphInfo.mBullet ) || 
					( mParagraphInfo.mNumber < startNo) )
					mParagraphInfo.mNumber = startNo ;
				mParagraphInfo.mBullet = tempBullet ;
			}
			break;
		}
		case eTagPntext :
		{
			// this is a prcursor to /*/pn style of tags so can ignore 
			if	(sPreviousTag == eTagOpenBrace ) 
			{
				ignoreGroup(pInd);
				pInd-- ; // this sets p = { so returns from this group
				// need to counter the effect of going back one
				sNoOpenGroups ++ ;
			}
			break ;
		}
		case eTagUd :
		{
			// could ignore group if dont understand ..
			ignoreGroup(pInd);	 // AE6555 Now we understand it, yes we will ignore the group
			break;
		}
		case eTagUc:
		{
			mTextInfo.mUcN = (qshort) sNumber;	// rmm5254
			mTextInfo.mSet |= WPTEXT_UCNSET;		// rmm5254
			break;
		}
		case eTagUpr :
		{
			ignoreNonUnicode(pInd);
			break;
		}
		// these are flags intend to ignore 
		case eTagFiletbl : case eTagHyphhotz :
		case eTagHyphcaps : case eTagPaperw : case eTagPaperh :
		case eTagExpnd : case eTagExpndtw : case eTagObject : 
		case eTagAuthor: case eTagOperator:	// rmmuni
		{
			break;
		}
		case eTagObjClass: case eTagObjData:
		{
			ignoreGroup(pInd);
			pInd-- ; // this sets p = { so returns from this group
			// need to counter the effect of going back one
			sNoOpenGroups ++ ;
			break;
		}
		
	
		// these are desination flags intend to ignore group 
		case eTagFooter : case eTagFooterf:  case eTagFooterl: 
		case eTagFooterr: case eTagFootnote: case eTagHeader : 
		case eTagHeaderf: case eTagHeaderl:  case eTagHeaderr :
		case eTagField : case eTagRevtbl  :
		case eTagTc :
		case eTagRxe :case eTagTxe :case eTagXe :
		{
			if ( sPreviousTag == eTagOpenBrace )
			{ 
				ignoreGroup(pInd);
				pInd-- ; // this sets p = { so returns from this group
				// need to counter the effect of going back one
				sNoOpenGroups ++ ;
			}
			break;
		}
		case eTagNonShppict :
		{
			ignoreGroup (pInd);
			pInd-- ; // this sets p = { so returns from this group
				// need to counter the effect of going back one
				sNoOpenGroups ++ ;
			break;
		}
		default :
			// not found in this set 
			foundFlag = qfalse ;
		break;
	}
	return foundFlag ;
}	

// rmm5254: convert a character to Unicode
qchar WPRtfClass::convertChar(qchar pChar)
{
	if (pChar <= 255)	// rmm_xc45: No need to check >= 0 as qchar is unsigned
	{
		if (mAnsiCodePageTable)
		{
			pChar = mAnsiCodePageTable[pChar];
		}
		else
		{
			// Assume Latin1 API character set if no code page specified
			qbyte b = (qbyte) pChar;
			CHRconvFromLatin1ApiBytes cfb(&b, 1);
			pChar = *cfb.dataPtr();
		}
		return pChar;
	}
	return 0x00BF; // return inverted question mark for characters we cannot convert
}

// This relies on the hex char to be put into the 
// field pointed to bt pInd. 
WPTextClass* WPRtfClass::createHexTxt( qlong &pInd, WPObjectClass* pParent )
{
	/* could use but not in ext comp	hex2intcat(&sTag[0], &mDocPtr[pInd] ); */
	qchar tempChar = (hexToNibble( mDocPtr[pInd] ) << 4  ) + hexToNibble( mDocPtr[pInd+1] );
	// rmm5254
	tempChar = convertChar(tempChar);
	qlong start = pInd -2 ;
	mDocPtr[start] = tempChar ;
	// This is so the databound object doesnt get confused but
	// I dont think it works.
	mDocPtr[start+1] =  RTF_OPEN_TAG; 
	mDocPtr[start+2] = 'z'; 
	qlong len = 1 ;
	
	WPTextClass* text = WPTextClass::make( pParent, this, &mTextInfo, start, len );
	// Start rmm5254
	if (sTagHaveBreak)
	{ 
		text->setBreak(qtrue); 
		sTagHaveBreak = qfalse;
	} 
	// End rmm5254
	pInd = pInd + 2;
	return text;
} // end of createHexTxt


WPTextClass* WPRtfClass::createUniTxt( qlong &pInd, WPObjectClass* pParent )
{
	WPTextClass* text = NULL;
	if ( sNumberSet )
	{
		// check we can understand this if not next tag should be hex value equiv.
		if ( sNumber > sMaxUnicodeNo )
		{
			// unicode number but cant cope with it so ignore this & 
			// look at asci value.
			return NULL ;
		}	 
		qlong pToSlash= pInd-1;
		qlong start = pInd - 1;
		for ( pToSlash=pInd-1 ;  ((pToSlash >0 ) && (mDocPtr[pToSlash] != RTF_OPEN_TAG));pToSlash-- );
		if ( mDocPtr[pToSlash] == RTF_OPEN_TAG )
		{
			mDocPtr[pToSlash] = (qchar)sNumber ;
			mDocPtr[pToSlash+1] = RTF_OPEN_TAG; 
			mDocPtr[pToSlash+2] = 'z';
			start = pToSlash;
		}
		else
			mDocPtr[start ] = (qchar)sNumber ;
		qlong len = 1 ;
		text = WPTextClass::make( pParent, this, &mTextInfo, start, len );
		// Start rmm5254
		if (sTagHaveBreak)
		{ 
			text->setBreak(qtrue); 
			sTagHaveBreak = qfalse;
		} 
		// End rmm5254
		// now ignore the hex equiv of the unicode
		if ( (mDocPtr[pInd] == RTF_OPEN_TAG) &&  ( mDocPtr[pInd+1] == '\'' ) )
		{
			mDocPtr[pInd+1] = 'z'; 
			mDocPtr[pInd+2] = 'z'; 
			mDocPtr[pInd+3] = 'z'; 
			pInd = pInd+4 ;
		}
		pInd += mTextInfo.mUcN;	// rmm5254: skip Ansi equivalent of Unicode character
	}
	return text;
} // end of createUniTxt

WPTextClass* WPRtfClass::parseRtftext( qlong& pInd, WPObjectClass* pParent )
{
	if ( sTagID == eTagHex )
		return( createHexTxt(pInd,pParent)) ;
	if ( sTagID == eTagU )
		return( createUniTxt(pInd,pParent)) ;

	qlong start = pInd;
	WPTextClass* text = NULL ;
	// what if get \{ Have ignored the \ set to tagChar need to pick up the
	// } { or '
	if (( mDocPtr[pInd] == RTF_CLOSE_GROUP ) || ( mDocPtr[pInd] == RTF_OPEN_GROUP )	
		|| ( mDocPtr[pInd] == '\'') ) // rmm5254: escaping via RTF_OPEN_TAG is handled below: ||(  mDocPtr[pInd] == RTF_OPEN_TAG ) )
	{
		pInd++;
	}
	if ( mDocPtr[pInd] == '\'' )
	{
		 // This is a hex val - should already be decoded
		pInd++; 
		return( createHexTxt(pInd,pParent)) ;
	}
	while ( pInd <= (qlong) mDocPtr.charLen() ) // AE6556 Was < 
	{
		// Start rmmuni
		qchar ch; qbool pastEnd;
		if (pInd == (qlong) mDocPtr.charLen()) // AE6556
		{	ch = RTF_CLOSE_GROUP;	pastEnd = qtrue; }
		else
		{	ch = mDocPtr[pInd];	pastEnd = qfalse;	}
		ch = convertChar(ch);	// rmm5254
		if ( !pastEnd )	// AE6556
			mDocPtr[pInd] = ch;
		// End rmmuni
		switch ( ch )
		{
			case RTF_CR_HEXD : case RTF_LF_HEXA :
			{
				mDocPtr[pInd] = RTF_STRIP_THIS_CHARACTER; // rmm5254: previously, this just set this to a space; CR and LF are not part of the document
				break;
			}
			case RTF_CLOSE_GROUP:
			case RTF_OPEN_GROUP :
			case RTF_OPEN_TAG :
			{
				// Start rmm5254: handle escaped {, }, \, CR and LF
				qlong nextInd = pInd + 1;
				if (RTF_OPEN_TAG == ch && nextInd < (qlong) mDocPtr.charLen())
				{
					qchar escapedCh = mDocPtr[nextInd];
					qbool done = qfalse;
					switch (escapedCh)
					{
						case RTF_OPEN_TAG:
						case RTF_CLOSE_GROUP:
						case RTF_OPEN_GROUP:
						case RTF_CR_HEXD:
						case RTF_LF_HEXA:
						{
							mDocPtr[pInd] = RTF_STRIP_THIS_CHARACTER;	// Strip the escaping backslash
							// Output actual character if necessary
							pInd = nextInd;
							if (RTF_OPEN_TAG == escapedCh)
								mDocPtr[pInd] = RTF_OPEN_TAG_CHAR;
							else if (RTF_OPEN_GROUP == escapedCh)
								mDocPtr[pInd] = RTF_OPEN_GROUP_CHAR;
							else if (RTF_CLOSE_GROUP == escapedCh)
								mDocPtr[pInd] = RTF_CLOSE_GROUP_CHAR;
							done = qtrue;
							break;
						}
					}
					if (done)
						break;
				}
				// End rmm5254
				qbool isTagChar = qfalse ;
				qlong len = pInd - start - pastEnd; // AE6556
				if ( RTF_OPEN_TAG )
					isTagChar = checkNotCharTag(pInd);
				if ( isTagChar )
					len++;
				if ((isTagChar ) &&(	sTagID == eTagTab ))
					len = len + 3 ;
				// now ignore last space 
				// rmm5254: this was losing spaces that were part of the document: if (( len> 0 )&&( mDocPtr[pInd-1] == ' ' ))
				// rmm5254: 	len --;
				if ( len > 0 )
				{
					// Start rmm5254: strip any CR or LF characters that are not required in the text
					qlong outputIndex = start;
					for (qlong inputIndex = start; inputIndex < (start + len); ++inputIndex)
					{
						qchar c = mDocPtr[inputIndex];
						if (c != RTF_STRIP_THIS_CHARACTER)
							mDocPtr[outputIndex++] = c;
					}
					len = outputIndex - start;
					// End rmm5254
					// Start rmm5254: if there are any cr's, lf's or crlf's, need to create separate text objects for them, with line breaks.
					qlong objStart = start;
					WPTextClass *text2;
					qbool needBreak = qfalse;
					for (qlong ind = start; ind < (start + len); ++ind)
					{
						if (mDocPtr[ind] == RTF_LF_HEXA || mDocPtr[ind] == RTF_CR_HEXD)
						{
							if (!(ind - objStart))
							{
								// Output a single space character, to ensure an empty line of the correct height is displayed
								mDocPtr[objStart] = ' ';
								text2 = WPTextClass::make(pParent, this, &mTextInfo, objStart, 1);
							}
							else text2 = WPTextClass::make(pParent, this, &mTextInfo, objStart, ind - objStart);
							if (needBreak) text2->setBreak(qtrue);
							else needBreak = qtrue;	// Ready for next time
							if (mDocPtr[ind] == RTF_CR_HEXD && (ind + 1) < (start + len) && mDocPtr[ind + 1] == RTF_LF_HEXA)
								++ind;	// Skip LF in CRLF
							objStart = ind + 1;
							if (text)
								text->orphanSetNext(text2);
							else 
								text = text2;
						}					
					}
					qlong remLen = ((len + start) - objStart);
					if (remLen > 0)
					{
						text2 = WPTextClass::make(pParent, this, &mTextInfo, objStart, remLen);		
						if (needBreak) 
						{
							text2->setBreak(qtrue);
							needBreak = qfalse;
						}
						if (text)
							text->orphanSetNext(text2);
						else 
							text = text2;
					}
					if (text)
					{
						if (sTagHaveBreak)
						{ 
							text->setBreak(qtrue); 
						} 
						sTagHaveBreak = needBreak;
					}
					// End rmm5254
				}
			  return text;
			} // end of case  end of string 
		} // end of char switch
		pInd++;
	} // end of while still more data
	return text;
} // end of parseRtftext

str255 WPRtfClass::readText(qlong& pInd, qbool ignoreSemiColon, qbool pFoundUprTag)
{
	qlong start = pInd;
	str255 text;
	while ( pInd < (qlong)mDocPtr.charLen() )
	{
		// Start rmmuni
		qchar ch = mDocPtr[pInd];
		ch = convertChar(ch);	// rmm5254
		mDocPtr[pInd] = ch;
		// End rmmuni
		switch ( ch )
		{
			case RTF_CR_HEXD : case RTF_LF_HEXA :
			{
				// this overwrites cr & lf a space.
				// rmm5254: mDocPtr[pInd] = ' ';
				break;
			}
			case RTF_SEMICOLON :
			{
				if ( ignoreSemiColon)
					break;
				// else fall into next.
			}

			case RTF_CLOSE_GROUP:
			case RTF_OPEN_GROUP :
			case RTF_OPEN_TAG :
			{
				qlong len = pInd - start;
				if ( len > 0 )
				{
					text = str255((qshort)len, &mDocPtr[start] );
					// Start rmm5254: strip CR and LF from the text - these are not part of the RTF data
					for (qshort i = text.length(); i >= 1; --i)
					{
						if (RTF_CR_HEXD == text[i] || RTF_LF_HEXA == text[i])
							text.deleet(i, 1);
					}
					// End rmm5254
				}
				// rmmuni: if we found the upr tag, while parsing this item, we need to look for Unicode text sequences
				if (pFoundUprTag)
				{
					qbool done = qfalse;
					WPTextClass *txtClass = 0;  
					while (!done && nextRTFtag(pInd, qfalse, qtrue)) // rmm5254
					{
						switch (sTagID)
						{
							case eTagOpenBrace: 
								break;
							case eTagCloseBrace:
								done = qtrue;
								break;
							case eTagCharacter :case eTagHex : case eTagU :
							{
								if ( txtClass != NULL )
								{
									WPTextClass*  text2 = parseRtftext(pInd,NULL);
									if ( text2 != NULL )
										txtClass->orphanSetNext(text2);
								}
								else 
								{
									txtClass = parseRtftext(pInd,NULL);
								}
								break;
							}
						}
					}
					// Get the text from txtClass and append it to text.
					while (txtClass)
					{
						text.concat(str255((qshort) txtClass->textLen(), getTextPtr(txtClass->textStart())));
						WPTextClass *tc = (WPTextClass *) txtClass->nextSibling();
						delete txtClass;
						txtClass = tc;
					}
				}
			  return text;
			} // end of case  end of string 
		} // end of char switch
		pInd++;
	} // end of while still more data
	return text;
} // end of readText

qbool WPRtfClass::checkNotCharTag(qlong& pInd)
{
	qlong startpind = pInd;

		while ( nextRTFtag(pInd,qfalse) )
		{				
			switch (sTagID)
			{
				case eTagEndash :
				{
					mDocPtr[startpind] = '-';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagEmdash :
				{
					mDocPtr[startpind] = '_';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagLquote :
				{
					mDocPtr[startpind] = '`';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagRquote :
				{
					mDocPtr[startpind] = '\'';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagLdblquote :
				{
					mDocPtr[startpind] = '"';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagRdblquote :
				{
					mDocPtr[startpind] = '\"';
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagTab :
				{
					mDocPtr[startpind] = ' ';
					mDocPtr[startpind + 1] = ' ';
					mDocPtr[startpind + 2] = ' ';
					mDocPtr[startpind + 3] = ' ';
					return qtrue ;
					break;
				}
				break;
				case eTagBullet : //set to 0xde = 222 or oxb7 = 183 = middle dot
				{
					// rmm5254:
					mDocPtr[startpind] = 0x2022;	// Unicode Bullet
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				case eTagHex :
				{
					mDocPtr[startpind] = (hexToNibble( mDocPtr[pInd] ) << 4 )+
																hexToNibble( mDocPtr[pInd+1] );
					pInd= pInd+2 ;
					mDocPtr[startpind+1] = RTF_OPEN_TAG; 
					mDocPtr[startpind+2] = 'z'; 
					return qtrue ;
					break;
				}
				default :
					// tag not found rewind
					pInd = startpind ;
					return qfalse ;
				break;
			} // end of switch 
		}// end of while next flag 
		// now reset to previous value :
		return qfalse ;
} // end of checkNotCharTag

WPTextClass* WPRtfClass::addSpecialChar( qlong &pInd, WPObjectClass* pParent )
{
	qlong start= pInd ;
//	qlong startpind= pInd ;
	WPTextClass* text = NULL ;

/*	if ( mDocPtr[start] == ' ' )
	{
		startpind = start ;
		pInd++ ;
	}
	else 
	{
		if (( sTagID == eTagHex ) && ( mDocPtr[start+2] == ' ' ))
			startpind = start +2 ;
		else // no obvious choice go back onto tag.
			startpind = start -1;
	}

*/
	// Now this can be databound stick char at beging of tag & then /z 
	// so rest is ignored
	qlong len = 1 ;
	switch ( sTagID)
	{
		case eTagEndash :
		{
			start = pInd - 7 ;
			mDocPtr[start] = '-';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagEmdash :
		{
			start = pInd - 7 ;
			mDocPtr[start] = '_';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagLquote :
		{
			start = pInd - 7 ;
			mDocPtr[start] = '`';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagRquote :
		{
			start = pInd - 7 ;
			mDocPtr[start] = '\'';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagLdblquote :
		{
			start = pInd - 10 ;
			mDocPtr[start] = '"';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagRdblquote :
		{
			start = pInd - 10 ;
			mDocPtr[start] = '"';
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagTab :
		{
			start = pInd - 4 ;
			mDocPtr[start] = ' ';
			mDocPtr[start+1] = ' ';
			mDocPtr[start+2] = ' ';
			mDocPtr[start+3] = ' ';
			len = 4 ;
			break;
		}
		case eTagBullet : // set to oxb7 = 183 = middle dot
		{
			start = pInd - 7 ;
			// rmm5254:
			mDocPtr[start] = 0x2022;	// Unicode Bullet
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			break;
		}
		case eTagHex :
		{
			start = pInd - 4 ;
			mDocPtr[start] = (hexToNibble( mDocPtr[pInd] ) << 4 ) +
												hexToNibble( mDocPtr[pInd+1] );
			mDocPtr[start+1] = RTF_OPEN_TAG; 
			mDocPtr[start+2] = 'z'; 
			pInd= pInd+2 ;
			break;
		}
		default :
			// dont know what we have return null
			return text;
		break;
	}
	text = WPTextClass::make( pParent, this, &mTextInfo, start, len );
	return text;
}
qbool WPRtfClass::parseRtfTableTags(WPTableInfoStruct &tableInfo)
{
	switch (sTagID )
	{
		case eTagCf :
		{
			mTextInfo.mTextColor = getRtfColor();
			mTextInfo.mSet |= WPTEXT_TEXTCOLORSET;
			break;
		}
		case eTagBrdrcf :
		{
			mCellInfo.mBorderColor = getRtfColor();
			break;
		}
		case eTagClvertalt ://Text is top-aligned in cell (the default).
			mCellInfo.mVAlign = eWPVAtexttop ;
			break ;
		case eTagClvertalc : // Text is centered vertically in cell.
			mCellInfo.mVAlign = eWPVAmiddle ;
			break ;
		case eTagClvertalb : // Text is bottom-aligned in cell
			mCellInfo.mVAlign = eWPVAbottom ;
		break ;
		case eTagBrdrw : // border width in twips 
			mCellInfo.mBorderSize = convTwipsToPixel(sNumber ) ;
		break;
		case eTagTrgaph : // half space bt cells of table row 
		{
			// no default given 
			if ( sNumberSet == qtrue)
				tableInfo.mCellPadding = convTwipsToPixel(sNumber) *2 ;
			break;
		}
		case eTagClmgf : // cell merge start
		{
			mCellInfo.mColSpan = 1 ;
			break;
		}
		case eTagClmrg : // Cell merged with previous
		{
			mCellInfo.mColSpan++ ;
			break;
		}
		case eTagClvmgf:
		{
			// cell mergred vert
			// need to add next text into this & 
			// this flag occurs after previous cellx ie cell2 max width
			// then cell3 is vert megered.
			break;
		}
		case eTagClvmrg:
		{
			// cell merged with prev vert.
			break;
		}
		case eTagTrql :
		{
			tableInfo.mHAlign = eWPHAleft ;
			break;
		}
		case eTagTrqr :
		{
			tableInfo.mHAlign = eWPHAright ;
			break;
		}
		case eTagTrqc :
		{
			tableInfo.mHAlign = eWPHAcenter ;
			break;
		}
		case eTagTrrh :
		{
			tableInfo.mHeight = convTwipsToPixel(sNumber);
			break;
		}
		case eTagClcbpat :
		{
			mCellInfo.mBackColor =  getRtfColor();;
			break;
		}
		default :
		{
			return qfalse;
		}
	}// end of switch on tagId	
	return qtrue;
}// end of parseRtfTableTags

void WPRtfClass::addParaToCell(WPParagraphClass* para, WPObjectClass *cell)
{
	WPParagraphClass* tempPara = para ;
	WPParagraphClass* tempNextPara = para ;
	while (tempNextPara != NULL)
	{
		tempNextPara = (WPParagraphClass *)tempPara->nextSibling() ;
		tempPara->insert(tempPara,cell,0) ;
		tempPara = tempNextPara ;
	}
}
void WPRtfClass::addTxtToPara(WPObjectClass* text,WPParagraphClass* para )
{
	
	WPObjectClass* tempText  = text ;
	WPObjectClass* tempNextText = tempText ;
	while (tempNextText != NULL)
	{
		tempNextText = (WPObjectClass *)tempText->nextSibling() ;
		tempText->insert(tempText,para,0) ;
		tempText = tempNextText ; 	
	}
}

// these functions may be member fns or changed place holders for skeleton
void WPRtfClass::readBinData(qlong& pInd)
{ 
	// need to skip to end of the binary data & igore this 
	// Assume that /bin has just been found  
	// Later may want to capture this data.
 
	qlong binLen = getDigit(pInd);
	// if first char a space then ignore this, 
	// then jump to end of bin data
	if ( mDocPtr[pInd] == ' ')
		pInd++;
	pInd  = pInd + binLen ;

}

// pkmacho
const str15 typeJPEGx(QTEXT("JPEG"));
const str15	typeGIFx(QTEXT("GIF"));
const str15 typePCXx(QTEXT("PCX"));
const str15 typePNGx(QTEXT("PNG"));

WPImageClass* WPRtfClass::readPictData(qlong& pInd)
{ 

	qlong start = pInd ;
	str15 imageType;
	qbool inBinFormat = qfalse; 
	qlong pictStartNoGroupsOpen = sNoOpenGroups ;
	// not accurate enough for real data 
	// need to find jpeg.
	WPImageClass* image = NULL ;
	qldim imHeight ;
	qldim imWidth ;
	while ( nextRTFtag(pInd) )
	{				
		switch (sTagID)
		{
			case eTagPar:	// rmm3975
			case eTagCloseBrace :
			{
				if (sNoOpenGroups < 	pictStartNoGroupsOpen )
				{
					return image;
				}
				break;
			}
			case eTagOpenBrace : case eTagSpace :
			{
				break;
			}
			case eTagPicwgoal :
			{	
				if ( sNumberSet )
					imWidth = convTwipsToPixel (sNumber);
				break;
			}
			case eTagPichgoal :
			{
				if ( sNumberSet )
					imHeight = convTwipsToPixel (sNumber);
				break;
			}
			case eTagJpegBlip : 
			{
				imageType = typeJPEGx ;
				break;
			}
			case eTagPngBlip :
			{
				// this has not been implemented.
				imageType = typePNGx ;
				break;
			}
			case	eTagCharacter : 
			{
				// Assume begining of text
				start = pInd;
				ignoreGroup(pInd);
				qlong end = pInd ;
				if ( imageType == typeJPEGx )
					image = createJpgFile( start,end,inBinFormat,imHeight,imWidth);
				else if ( imageType == typePNGx )
				{
					WPObjectClass* pParent = NULL; 
					WPImageInfoStruct imageInfo;
					imageInfo.mHeight = imHeight ;
					imageInfo.mWidth = imWidth;
					image=WPImageClass::make( pParent,this,&imageInfo,start,(end-start+1));
				}
				break;
			}
			case eTagUnknown:
			{
				if ( sPreviousTag == eTagStar )
				{
					ignoreGroup(pInd);
					if (sNoOpenGroups < 	pictStartNoGroupsOpen )
						return image ;
				}
				break;
			}
			case eTagBin :
			{
				inBinFormat = qtrue;
				break;
			}
			default :
			{
				// ignore tag 
			}
		}
	}
	return image ;
}
// mlr0024 change fullpath to fullPathStr to avoid confusion as
// an existing method & fix linux  problem
void createTempFileName(str255 &fullPathStr)
{
  // These come from the respective xfile.cpp createtemp lines.
	#if defined(ismac)
		str255 fullDir;
		#ifndef ismacosx
			short  foundVol;
			long tempDir ;	
			// This gets the temp folder & then extracts the name by going up the tree
			qlong err=FindFolder(kOnSystemDisk,kTemporaryFolderType,kCreateFolder,&foundVol,&tempDir);
			CInfoPBRec pb; 
			str255 curDirName;
			DirInfo &db = pb.dirInfo;
			fillc( &pb, sizeof(CInfoPBRec), 0 );
			db.ioNamePtr = curDirName.pString();
			db.ioVRefNum = foundVol;
			db.ioDrParID = tempDir;
			db.ioFDirIndex = -1;
			while ( db.ioDrParID )
			{
				db.ioDrDirID = db.ioDrParID;
				err = PBGetCatInfo( &pb, false );
				if ( err != noErr ) break;
				fullDir.insert( ':', 1 );
				fullDir.insert( curDirName, 1 );
			} // while ( db.ioDrParID != fsRtDirID );
		#else
			// rmmuni_osx7:
			ECOosxGetTempFolderPath(fullDir);	// rmmuni_osx9
		#endif
				
		str255 strname = str15(QTEXT("OM"));
		str15 longStr ;
		double d1 = rand();
		if ( d1 > 100 ) 
			d1= d1/100 ;
		qlongToString((qlong)d1,longStr);
		strname.concat(longStr,str15(QTEXT(".JPG")));
		fullPathStr = fullDir ;
		fullPathStr.concat(strname);
		quint noTrys=0;
		while (FILEexists( fullPathStr ) )
		{
			// get a new random no.
			noTrys ++ ;
			if ( noTrys > 50 )
			{
				break;
			}
			else
			{
				fullPathStr = fullDir ;
				qlongToString((qlong)(rand()/100.0),longStr);
				fullPathStr.concat(longStr,str15(QTEXT(".JPG")));
			}
		}
		return ;
	#elif defined(isunix)
		// not tried
		fullPathStr = str15("/tmp/OM");
		str15 longStr ;
		double d1 = rand();
		if (d1 > 100) 
			d1= d1/100 ;
		qlongToString((qlong)d1,longStr);
		fullPathStr.concat(longStr,str15(".JPG"));
		quint noTrys=0;
		while ( FILEexists( fullPathStr ) )
		{
			noTrys ++ ;
			if ( noTrys > 50 )
			{
				break;
			}
			else
			{
				fullPathStr = str15("/tmp/OM");
				str15 longStr ;
				qlongToString((qlong)(rand()/100.0),longStr);
				fullPathStr.concat(longStr,str15(".JPG"));
			}
		}
		return  ;
	#else
		// Windows
		qoschar tmpbuff[255];
		#ifdef iswin32
			qoschar szTmpPath[_MAX_PATH];
			if( !GetTempPath(_MAX_PATH,szTmpPath) )
				_tcscpy(szTmpPath,(qoschar *) QTEXT("."));
  		GetTempFileName(szTmpPath,(qoschar *) QTEXT("OM"),0,tmpbuff); 
		#else
			GetTempFileName(0,"OM",0,(LPTSTR)tmpbuff);
		#endif
		// note the above creates a c:\windows\OM*.tmp file
		// we need a *.jpg file so delete this & redo
		
		fullPathStr = QOSTEXT(tmpbuff);
		EXTfile pictFile ;
		pictFile.deleet(fullPathStr);
		qshort posOfDot = fullPathStr.pos('.') ;
		if ( posOfDot )
		{
			fullPathStr.deleet ( posOfDot,(qshort)( fullPathStr.length() - posOfDot +1));
			fullPathStr.concat(str15(QTEXT(".JPG")));
		}
		while ( FILEexists( fullPathStr ) )
		{
			// note as temp file name is ctreated from clock tick &
			// we dont keep the same extension - need to check & redo.
			fullPathStr.deleet ( posOfDot,(qshort)( fullPathStr.length() - posOfDot +1));
			qlong l1= (qlong)((double)( rand()*100.0)/(double)RAND_MAX); //mlr0024 get rid of warning
			// this gives a random number from 0 to ox7fff = 32767
			// 0 - 99 will do .
			str15 longStr ;
			qlongToString(l1,longStr);
			fullPathStr.concat(longStr);
			fullPathStr.concat(str15(QTEXT(".JPG")));
		}
		return ;
	#endif
}
WPImageClass* WPRtfClass::createJpgFile(qlong start, qlong end,qbool inBinFormat, qldim imHeight,qldim imWidth )
{
	WPImageInfoStruct imageInfo;
	imageInfo.mHeight = imHeight ;
	imageInfo.mWidth = imWidth;
	str255 strFilename ;
	qret fileCreated ;
	createTempFileName(strFilename) ;
	EXTfile pictFile;
	fileCreated = pictFile.create(strFilename, qfalse) ;
	qret fileWritten = qfalse ; 
	if (( fileCreated == e_ok ) && ( inBinFormat == qfalse ))
	{
		char  *tempChar = new char[end - start+1]  ;
		qlong charNo=0 ;
		for (qlong index = start ; index < end ; index=index+2 )
		{
			if ( (mDocPtr[index]  == RTF_CR_HEXD )|| 
			( mDocPtr[index]  == RTF_LF_HEXA ))
			{
				index-- ;
			}
			else
			{
				tempChar[charNo] = char((hexToNibble( mDocPtr[index] ) << 4  ) + hexToNibble( mDocPtr[index+1] ));
				charNo++ ;
			}
		}
		tempChar[charNo] = '\0';
		fileWritten  = pictFile.write	( (void *)&tempChar[0],0, charNo );
		delete [] tempChar ;
	}
	else if ( fileCreated == e_ok )
		fileWritten = pictFile.write	( (void *)&mDocPtr[start],0, end - start );
	if ( fileWritten == e_ok )
	{
		pictFile.getName(strFilename, qtrue );
		pictFile.close();
		imageInfo.mTextInfo = mTextInfo;
		WPObjectClass* pParent = NULL; 
		WPImageClass*	image = WPImageClass::make( pParent, this, &imageInfo,
	 		 &strFilename[1], strFilename[0] );
		if ( mFilesCreated )
			mFilesCreated->addFile(strFilename);
		else
			mFilesCreated = new wpRtfImgFilesCreated(	strFilename );
		return image ;
	}
	return NULL ;
}


qshort WPRtfClass::convTwipsToPixel(qlong twipNumber)
{
	// need to convert twips, a twentieth of a point ,
	// a point = 1/72 inch, to pixels 
	//return twipNumber * (Avg GDIgetScreenResolution)/(20*72);
	return (qshort)(twipNumber * sSceenRes) ;
}

qlong  WPRtfClass::getDigit(qlong& pInd )
{
// this assumes that we start on a number or hyphen 
// & need next number

	sNumber = 0 ;
	if ( mDocPtr[pInd] == RTF_HYPHEN  )
	{ 
		pInd++ ;
	}
	if ( wpRtfIsdigit (mDocPtr[pInd])  )
	{
		qlong ptrDig ;
		for ( ptrDig = pInd ; ( ptrDig < (qlong)mDocPtr.charLen() 
			&&	wpRtfIsdigit (mDocPtr[ptrDig])); ptrDig ++ )
		{
		}
		str15 pVal = str15((qshort)(ptrDig - pInd) , &mDocPtr[pInd] );
		// returns true if valid number
		if ( stringToQlong( pVal, sNumber ) )
			sNumberSet = qtrue;
		pInd = ptrDig;
	} // end wpRtfIsdigit
	return sNumber;
} // end of getDigit

void  WPRtfClass::resetDefaultParaProperties(void)
{
	// These defaults taken from the constructor of WPParagraphInfoStruct
	// perhaps should be from style 0.
	// this resets this in case used in list
	mParagraphInfo.mIndent = 0 ;
	setParaStyle(0);
	setCharStyle(0);
	mParagraphInfo.mBullet = eWPbulletNone;
	mParagraphInfo.mNumber = 0;
}

// AE6556
void WPRtfClass::endPara(qshort& lastBulletNo, qlong& lastBulletNumStyle, qbool& inTable, WPObjectClass*& text,
												 WPParagraphClass*& para,	WPTableClass*& table)
{
	if (( !inTable ) && (table != NULL))
	{
		// we were in a table & now we are not
		table = NULL ;
	}
	if ( sInList )
	{
		// note cant use mBullet as not well defined
		if ( lastBulletNumStyle == sThisBulletNumStyle )
		{ 
			mParagraphInfo.mNumber = ++lastBulletNo;
		}
		else
		{
			lastBulletNumStyle = sThisBulletNumStyle ;
			lastBulletNo = mParagraphInfo.mNumber;
		}
	}
	// Note the drawing will fail if no text in bullet 
	if ( text == NULL )
		mParagraphInfo.mBullet = eWPbulletNone;
	
	// if not in table parent = document, if in a table parent will 
	// be cell - but later.
	WPObjectClass* pParent= NULL;
	if ( !inTable ) 
		pParent = this ;
	
	WPParagraphClass * para2= WPParagraphClass::make( pParent,this, &mParagraphInfo );
	if ( text != NULL)
	{
		addTxtToPara(text,para2 );
		text= NULL;
	}
	if ( inTable )
	{
		// need to add para to paralist.
		if ( para != NULL )
			para->orphanSetNext(para2);
		else
			para = para2 ;
	}
	sInList = qfalse;
}

void WPRtfClass::getStyleSheet(qlong& pInd)
{
	qlong styleTableStartNoGroupsOpen = sNoOpenGroups ;
	pushParaSettings();
	pushTextSettings();
	qlong styleNo = 0;
	eTagID styleType = eTagS ; //( text,para )
	qlong setFlag = 0;
	qbool firstPar = qtrue;
	qbool firstText = qtrue;
	mStyleParaTab = new wpRtfParaStyleTab();
	wpRtfParaStyleTab * style = mStyleParaTab ;
	mStyleTextTab = new wpRtfTextStyleTab();
	wpRtfTextStyleTab * textStyle = mStyleTextTab ;
	// this sets rtf defaults in style 0 
	mParagraphInfo.mHAlign = eWPHAleft ;
	mParagraphInfo.mSpaceAfter = 0 ;
	mParagraphInfo.mSpaceBefore = 0 ;
	setFlag = setFlag | WPARA_HALIGN | WPARA_SPBEFORE | WPARA_SPAFTER ;
	qbool hasFont = qfalse, hasFontSize = qfalse;	// rmm5254
	while ( nextRTFtag(pInd) )
	{				
		switch (sTagID)
		{
			case eTagS: // paragraph style includes 
			{
				styleNo = sNumber ;
				styleType = eTagS ;
				break;
			}
			case eTagCs: // char or text style
			{
				styleNo = sNumber ;
				styleType = eTagCs;
				break;
			}
			break;
			case eTagOpenBrace :
			{
				pushTextSettings();
				pushParaSettings();
				break;
			}
			case eTagCloseBrace :
			{
				popParaSettings();
				popTextSettings();
				if (sNoOpenGroups < 	styleTableStartNoGroupsOpen )	
					return;
				break;
			}
			case eTagSemiColon :
			{
				// end of this item
				if (styleType == eTagS)
				{
					setDefaultFont(!hasFont, !hasFontSize);	// rmm5254
					mParagraphInfo.mTextInfo = mTextInfo; 
					if ( firstPar == qtrue )
					{
						style->setParaStyle(styleNo,&mParagraphInfo,setFlag);
						textStyle->setTextStyle(styleNo,&mTextInfo);
						firstPar = qfalse ;
						firstText= qfalse ;
					}
					else
						style = style->addParaStyle(styleNo,&mParagraphInfo,setFlag);
				}
				else if (styleType == eTagCs)
				{
					setDefaultFont(!hasFont, !hasFontSize);	// rmm5254
					WPTextInfoStruct textInfo ;
					textInfo = mTextInfo ;
					if ( firstText == qtrue )
					{
						textStyle->setTextStyle(styleNo,&textInfo);
						firstText = qfalse ;
					}
					else
						textStyle = textStyle->addTextStyle(styleNo,&textInfo);
				}
				styleType = eTagUnknown;
				hasFont = hasFontSize = qfalse;	// rmm5254
				break;
			}
			case eTagQc: case eTagQj: case eTagQl: case eTagQr:
			{
				parseGeneralTags(pInd);
				setFlag |= WPARA_HALIGN ;
				break;
			}			
			case eTagLi: // line indent
			{
				parseGeneralTags(pInd);
				setFlag |= WPARA_INDENT ;
				break;
			}			
			case eTagSa : // space after
			{
				parseGeneralTags(pInd);
				setFlag |= WPARA_SPAFTER ;
				break;
			}
			case eTagSb :
			{
				parseGeneralTags(pInd);
				setFlag |= WPARA_SPBEFORE ;
				break;
			}			
			case eTagB :case eTagI : case eTagSub: 
			case eTagSuper : case eTagNoSupersub :
			case eTagUl : case eTagUlw : case eTagUld : 
			case eTagUldb : case eTagUlnone :case eTagStrike :
			case eTagFs: case eTagCf :case eTagF :
			{
				// Start rmm5254
				if (eTagF == sTagID) hasFont = qtrue;
				else if (eTagFs == sTagID) hasFontSize = qtrue;
				// End rmm5254
				parseGeneralTags(pInd);
				break;
				// these are text items Flag set inside
			}
			case eTagCharacter :
			{
				str31 styleString = readText(pInd,qfalse);
        GDIignore(&styleString);
				break;
			}
		} // end of switch
	} //end of while
	popParaSettings();
	popTextSettings();
	return;
} // end of getStyleSheet
				
void WPRtfClass::skipToBraceOrSpace(qlong& pInd)
{
	// rmm4177: also need to stop at carriage return or linefeed.
	// walk along string until come to } { or space
	while ( pInd < (qlong)mDocPtr.charLen() )
	{		
		if (mDocPtr[pInd] == RTF_OPEN_GROUP || mDocPtr[pInd] == RTF_CLOSE_GROUP || mDocPtr[pInd] == ' ' || 
				mDocPtr[pInd] == RTF_CR_HEXD || mDocPtr[pInd] == RTF_LF_HEXA)
		{
			break;
		}
		pInd++ ;
  }
}

void WPRtfClass::skipToCloseBrace(qlong& pInd) // rmmuni
{
	while ( pInd < (qlong)mDocPtr.charLen() )
	{		
		if (mDocPtr[pInd] == RTF_CLOSE_GROUP)
		{
			break;
		}
		pInd++ ;
  }
}

qcol WPRtfClass::getRtfColor()
{
	if (( sNumberSet ==  qtrue ) &&  ( mColorTable != NULL ) )
	{
		// look up number in color table
		wpRtfColorTab* currentCol = mColorTable->findColor(sNumber );
		if (currentCol != NULL )
			return GDImakeQcol(currentCol->getRed(),currentCol->getGreen(),
				currentCol->getBlue()); 
	}
	// ie if cant find color or number not set.
	return GDI_COLOR_QBLACK;

}



//
void WPRtfClass::readColorTab(qlong& pInd )
{
	// have just got the \colortab lable. 
	// store a local color table for use in processing
	qlong LocalRed = 0;
	qlong LocalGreen = 0 ;
	qlong LocalBlue = 0 ;
	qshort localLevel = 0 ;
	mColorTable = new wpRtfColorTab();
	wpRtfColorTab * colorTab = mColorTable ;
	qbool firstCol = qtrue;

	while ( nextRTFtag(pInd) )
	{				
		switch (sTagID)
		{
			case eTagRed :
			{
				if ( sNumberSet )
					LocalRed = sNumber ;
				else 
					LocalRed = 0 ;
				break;
			}
			case eTagGreen :
			{
				if ( sNumberSet )
					LocalGreen = sNumber ;
				else 
					LocalGreen = 0 ;
				break;
			}
			case eTagBlue :
			{
				if ( sNumberSet )
					LocalBlue = sNumber ;
				else 
					LocalBlue = 0 ;
					break;
			}
			case eTagSemiColon :
			{
						// end of group create color ;
				if (firstCol )
				{
					colorTab->setColor(LocalRed,LocalGreen,LocalBlue);
					firstCol= qfalse ;
				}
				else
					colorTab = colorTab->addColor(LocalRed,LocalGreen,LocalBlue );
				LocalRed = 0;
				LocalGreen = 0 ;
				LocalBlue = 0 ;
				break;
			}
			case eTagOpenBrace : 
			{
				localLevel ++ ;
				break;
			}
			case eTagCloseBrace:
			{
				if (localLevel < 1 )
					return ;
				localLevel -- ;
				break;
			}
		}// end of switch on tagId 
	} // end of nextRTFtag 
}
void WPRtfClass::pushTextSettings(void)
{
	push_style();
	push_fontNum();
	push_fontSize();
	push_bold();
	push_italic(); 
	push_underline();
	push_strike();
	push_sub();
	push_super(); 
	push_textColor();
	push_UcN();					// rmm5254
	push_codePage();	// rmm5254
}
		
void WPRtfClass::popTextSettings(void)
{
		pop_style();
		pop_fontNum(); 
		pop_fontSize();
		pop_bold(); 
		pop_italic();
		pop_underline(); 
		pop_strike(); 
		pop_sub(); 
		pop_super(); 
		pop_textColor(); 
		pop_UcN();				// rmm5254
		pop_codePage();	// rmm5254
}

void WPRtfClass::pushParaSettings(void)
{
	push_bullet();
	push_indent();
	push_bulletNo();
	push_halign();
}
void WPRtfClass::popParaSettings(void)
{
	pop_halign();
	pop_indent();
	pop_bullet();
	pop_bulletNo();

}
void WPRtfClass::setCharStyle(qlong styleNum)
{
	if (mStyleTextTab == NULL )
		return ;
	WPTextInfoStruct * textInfo ;
	textInfo = mStyleTextTab->findTextStyle(styleNum);
	if ( textInfo != NULL )
	{
		if (textInfo->mSet & WPTEXT_STYLESET	)
			mTextInfo.mFontSize = textInfo->mFontSize ;
		if (textInfo->mSet & 	WPTEXT_FONTNUMSET	)
			mTextInfo.mFontNum = textInfo->mFontNum ;		
		if (textInfo->mSet & WPTEXT_FONTSIZESET )
			mTextInfo.mFontSize = textInfo->mFontSize ;
		if (textInfo->mSet & WPTEXT_FONTSIZEMODE )
			mTextInfo.mFontSizeMode = textInfo->mFontSizeMode ;	
		if (textInfo->mSet & WPTEXT_FONTSTYBSET)
			mTextInfo.mFontStyBold = textInfo->mFontStyBold ;
		if (textInfo->mSet & WPTEXT_FONTSTYISET)	
			mTextInfo.mFontStyItalic = textInfo->mFontStyItalic ;
		if (textInfo->mSet & WPTEXT_FONTSTYUSET)	
			mTextInfo.mFontStyUnderline = textInfo->mFontStyUnderline ;
		if (textInfo->mSet & WPTEXT_FONTSTRIKESET)	
			mTextInfo.mFontStrike = textInfo->mFontStrike ;
		if (textInfo->mSet & WPTEXT_FONTSUBSET	)	
			mTextInfo.mFontSub = textInfo->mFontSub ;
		if (textInfo->mSet & WPTEXT_FONTSUPERSET	)
			mTextInfo.mFontSuper = textInfo->mFontSuper ;
		if (textInfo->mSet & WPTEXT_TEXTCOLORSET)	
			mTextInfo.mFontSize = textInfo->mFontSize ;
		mTextInfo.mSet =  mTextInfo.mSet | textInfo->mSet ;
	}
}
void WPRtfClass::setParaStyle(qlong styleNum)
{
	if (mStyleParaTab == NULL )
		return ;
	wpRtfParaStyleTab *parStyle = mStyleParaTab->findParaStyle(styleNum);
	if ( parStyle != NULL )
	{
		WPParagraphInfoStruct * parInfo = parStyle->getParInfoStruct() ;
		WPTextInfoStruct * textInfo  = &( parInfo->mTextInfo) ;
		if (( textInfo != NULL ) && ( textInfo->mSet != 0 ))
		{
			if (textInfo->mSet & WPTEXT_STYLESET	)
				mTextInfo.mFontSize = textInfo->mFontSize ;
			if (textInfo->mSet & 	WPTEXT_FONTNUMSET	)
				mTextInfo.mFontNum = textInfo->mFontNum ;		
			if (textInfo->mSet & WPTEXT_FONTSIZESET )
				mTextInfo.mFontSize = textInfo->mFontSize ;
			if (textInfo->mSet & WPTEXT_FONTSIZEMODE )
				mTextInfo.mFontSizeMode = textInfo->mFontSizeMode ;	
			if (textInfo->mSet & WPTEXT_FONTSTYBSET)
				mTextInfo.mFontStyBold = textInfo->mFontStyBold ;
			if (textInfo->mSet & WPTEXT_FONTSTYISET)	
				mTextInfo.mFontStyItalic = textInfo->mFontStyItalic ;
			if (textInfo->mSet & WPTEXT_FONTSTYUSET)	
				mTextInfo.mFontStyUnderline = textInfo->mFontStyUnderline ;
			if (textInfo->mSet & WPTEXT_FONTSTRIKESET)	
				mTextInfo.mFontStrike = textInfo->mFontStrike ;
			if (textInfo->mSet & WPTEXT_FONTSUBSET	)	
				mTextInfo.mFontSub = textInfo->mFontSub ;
			if (textInfo->mSet & WPTEXT_FONTSUPERSET	)
				mTextInfo.mFontSuper = textInfo->mFontSuper ;
			if (textInfo->mSet & WPTEXT_TEXTCOLORSET)	
				mTextInfo.mFontSize = textInfo->mFontSize ;
			mTextInfo.mSet =  mTextInfo.mSet | textInfo->mSet ;
		}
		
		qlong setFlags = parStyle->getSetFlag();
		if (setFlags & WPARA_HALIGN)
				mParagraphInfo.mHAlign =  parInfo-> mHAlign;
		if (setFlags & WPARA_INDENT)
				mParagraphInfo.mIndent =  parInfo-> mIndent;
		if (setFlags & WPARA_SPAFTER)
				mParagraphInfo.mSpaceAfter =  parInfo-> mSpaceAfter;
		if (setFlags & WPARA_SPBEFORE)
				mParagraphInfo.mSpaceBefore =  parInfo->mSpaceBefore ;
		if (setFlags & WPARA_NUMBER)
				mParagraphInfo.mNumber =  parInfo-> mNumber;
		if (setFlags & WPARA_BULLET)
				mParagraphInfo.mBullet =  parInfo->mBullet ;	
	}
	return;
}

const str15 rtfBack(QTEXT(".."));

#if defined(ismac)
	#define dirSep ':'
	const str15 driveSep(QTEXT(":"));
#elif defined(isunix)
	#define dirSep '/'
	const str15 driveSep(QTEXT("/"));
#else
	#define dirSep '\\'
	const str15 driveSep(QTEXT(":\\"));
#endif
qbool WPRtfClass::getBookmark( str255& pBookmark )
{
	// Not sure what this is but defined by header class
	return qfalse;
}
qbool WPRtfClass::fullPath(str255& pPath, qbool pApi) // rmm10755
{
	if ((!pPath.pos( rtfBack )) &&
	( pPath.pos( dirSep ) || pPath.pos( dirSep )  )) return qtrue;

   // this is used if rtf refers to a jpg image
	str255 docPath = mDocURL;
	extractPath( docPath );
	appendPath( docPath, pPath );
	pPath = docPath;
	return qtrue;
}
qbool WPRtfClass::extractPath( str255& pPath )
{

	qshort cur = pPath.length();
	while ( cur )
	{
		if ( pPath[cur] == dirSep )
		{
			pPath[0] =(qchar) cur;
			return qtrue;
		}
		cur--;
	}
	return qfalse;
}
qbool WPRtfClass::appendPath( str255& pPath, str255 pSubPath )
{
	str255 sub;
	if ( pPath[pPath.length()] == dirSep ) pPath.deleet( pPath.length(), 1 );
	while ( pSubPath.length() )
	{
		qshort p = pSubPath.pos( dirSep );
		if ( p )
		{
			sub.copy( pSubPath, 1, p-1 );
			pSubPath.deleet( 1, p );
		}
		else
		{
			sub = pSubPath;
			pSubPath = qnil;
		}
		if ( sub == rtfBack )
		{
			qshort cur = pPath.length();
			while ( cur && pPath[cur] != dirSep ) cur--;
			if ( cur )
			{
				pPath.deleet( cur, 255 );
			}
		}
		else
		{
			pPath.concat( dirSep );
			pPath.concat( sub );
		}
	}
	return qtrue;
}
// At this point we have {\upr then we need to ignore {various bits}
// and restart at \*\ud this is a unicode adaption.
// Otherwise everything appears twice.
void WPRtfClass::ignoreNonUnicode(qlong& pInd)
{
	qlong ignoreNonUnicodeGoupStart = sNoOpenGroups ;
	qbool doneGroup = qfalse;
	while ( !doneGroup && nextRTFtag(pInd) )
	{	
		switch (sTagID )
		{
			case eTagUd :
			{
				// what we were waiting for :
				return;
			}
			case eTagSpace :
			{
				// ignore this
				break;
			}
			case eTagOpenBrace :
			{
				if (!doneGroup) 
				{
					ignoreGroup(pInd);
					doneGroup = qtrue;
				}
				break;
			}
			case eTagCloseBrace :
			{
				// unexpected ensure not gone up too far.
				if ( sNoOpenGroups <  ignoreNonUnicodeGoupStart )
					return ;
			}
			case eTagCharacter :
			{
				// note if dont catch loops !
				skipToBraceOrSpace(pInd);
				break;
			}
			default :
			{
				// unexpected ignore 
			}
		} // end of switch
	} // end of while
} // end of ignoreNonUnicode

// Start rmmuni
// Start rmm9881
qlong WPRtfClass::smReplaceTabsWithSpacesWhenAddingToPrintJob = 0;	
qchar	WPRtfClass::smSpaces[cMaxTabSpaces];
// End rmm9881
void WPRtfClass::init()
{
	// Start rmm9881: Read tab space replace configuration
	static const char *sCfgGroup = "docview";
	static const char *sReplaceTabsWithSpaces = "replaceTabsInRTFwithSpacesWhenAddingToReport";

	if (!ECOhasConfigurationItem(sCfgGroup, sReplaceTabsWithSpaces))
	{
		smReplaceTabsWithSpacesWhenAddingToPrintJob = 2;	// Default to 2 (zero means do not replace)
		ECOwriteIntegerConfigurationItem(sCfgGroup, sReplaceTabsWithSpaces, smReplaceTabsWithSpacesWhenAddingToPrintJob);
	}
	else
	{
		smReplaceTabsWithSpacesWhenAddingToPrintJob = ECOreadIntegerConfigurationItem(sCfgGroup, sReplaceTabsWithSpaces, 2);
		if (smReplaceTabsWithSpacesWhenAddingToPrintJob < 0)
			smReplaceTabsWithSpacesWhenAddingToPrintJob = 0;
		else if (smReplaceTabsWithSpacesWhenAddingToPrintJob > cMaxTabSpaces)
			smReplaceTabsWithSpacesWhenAddingToPrintJob = cMaxTabSpaces;
	}

	MEMmemFillChar(smSpaces, cMaxTabSpaces, ' ');
	// End rmm9881
}

static char* sRTFtags = NULL;
static qlong sRTFtagsLen = 0;
void WPRtfClass::initTags()
{
	sRTFtags = new char[strlen(sRtfSTRING1)+strlen(sRtfSTRING2)+strlen(sRtfSTRING3)+1]; *sRTFtags = 0;	// rmm5254
	strcat(sRTFtags,sRtfSTRING1); strcat(sRTFtags,sRtfSTRING2); strcat(sRTFtags,sRtfSTRING3);						// rmm5254
	sRTFtagsLen = (qlong) strlen( sRTFtags );	// rmm64bit5
	sRTFtagsCfu = new CHRconvFromBytes((qbyte *) sRTFtags, sRTFtagsLen);
}

void WPRtfClass::term()
{
	// Start rmm5254
	if (sWinFontList)
	{
		*sWinFontList = qnil;
		delete sWinFontList;
		sWinFontList = 0;
	}
	if (sRepFontList)
	{
		*sRepFontList = qnil;
		delete sRepFontList;
		sRepFontList = 0;
	}
	// End rmm5254
	if (sRTFtagsCfu) delete sRTFtagsCfu;
	if (sRTFtags) delete [] sRTFtags;
	
	// Start CR0472
	sRTFtagsCfu = NULL;
	sRTFtags = NULL;
	// End CR0472
}

void WPRtfClass::getTitle( EXTfldval& pFval )
{
	if ( !mDocPtr )
		pFval.setChar((qchar*)QTEXT(""),0); 
	else
		pFval.setChar(mTitle); 
}
// End rmmuni

// End of file
