context: // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  //
  // Authors:
  //	Peter Bartok	(pbartok@novell.com)
  //
  
  // COMPLETE
  
  #undef RTF_DEBUG
  
  using System;
  using System.Collections;
  using System.IO;
  using System.Text;
  
  namespace System.Windows.Forms.RTF {
  	internal class RTF {
  		#region	Local Variables
  		internal const char	EOF = unchecked((char)-1);
  		internal const int	NoParam = -1000000;
  		internal const int	DefaultEncodingCodePage = 1252;
  
  		private TokenClass	rtf_class;
  		private Major		major;
  		private Minor		minor;
  		private int		param;
  		private string encoded_text;
  		private Encoding encoding;
  		private int encoding_code_page = DefaultEncodingCodePage;
  		private StringBuilder	text_buffer;
  		private Picture picture;
  		private int		line_num;
  		private int		line_pos;
  
  		private char		pushed_char;
  		//private StringBuilder	pushed_text_buffer;
  		private TokenClass	pushed_class;
  		private Major		pushed_major;
  		private Minor		pushed_minor;
  		private int		pushed_param;
  
  		private char		prev_char;
  		private bool		bump_line;
  
  		private Font		font_list;
  
  		private Charset		cur_charset;
  		private Stack		charset_stack;
  
  		private Style		styles;
  		private Color		colors;
  		private Font		fonts;
  
  		private StreamReader	source;
  
  		private static Hashtable	key_table;
  		private static KeyStruct[]	Keys = KeysInit.Init();
  
  		private DestinationCallback	destination_callbacks;
  		private ClassCallback		class_callbacks;
  		#endregion	// Local Variables
  
  		#region Constructors
  		static RTF() {
  			key_table = new Hashtable(Keys.Length);
  			for (int i = 0; i < Keys.Length; i++) {
  				key_table[Keys[i].Symbol] = Keys[i];
  			}
  		}
  
  		public RTF(Stream stream) {
  			source = new StreamReader(stream);
  
  			text_buffer = new StringBuilder(1024);
  			//pushed_text_buffer = new StringBuilder(1024);
  
  			rtf_class = TokenClass.None;
  			pushed_class = TokenClass.None;
  			pushed_char = unchecked((char)-1);
  
  			line_num = 0;
  			line_pos = 0;
  			prev_char = unchecked((char)-1);
  			bump_line = false;
  			font_list = null;
  			charset_stack = null;
  
  			cur_charset = new Charset();
  
  			destination_callbacks = new DestinationCallback();
  			class_callbacks = new ClassCallback();
  
  			destination_callbacks [Minor.OptDest] = new DestinationDelegate (HandleOptDest);
  			destination_callbacks[Minor.FontTbl] = new DestinationDelegate(ReadFontTbl);
  			destination_callbacks[Minor.ColorTbl] = new DestinationDelegate(ReadColorTbl);
  			destination_callbacks[Minor.StyleSheet] = new DestinationDelegate(ReadStyleSheet);
  			destination_callbacks[Minor.Info] = new DestinationDelegate(ReadInfoGroup);
  			destination_callbacks[Minor.Pict] = new DestinationDelegate(ReadPictGroup);
  			destination_callbacks[Minor.Object] = new DestinationDelegate(ReadObjGroup);
  		}
  		#endregion	// Constructors
  
  		#region Properties
  		public TokenClass TokenClass {
  			get {
  				return this.rtf_class;
  			}
  
  			set {
  				this.rtf_class = value;
  			}
  		}
  
  		public Major Major {
  			get {
  				return this.major;
  			}
  
  			set {
  				this.major = value;
  			}
  		}
  
  		public Minor Minor {
  			get {
  				return this.minor;
  			}
  
  			set {
  				this.minor = value;
  			}
  		}
  
  		public int Param {
  			get {
  				return this.param;
  			}
  
  			set {
  				this.param = value;
  			}
  		}
  
  		public string Text {
  			get {
  				return this.text_buffer.ToString();
  			}
  
  			set {
  				if (value == null) {
  					this.text_buffer.Length = 0;
  				} else {
  					this.text_buffer = new StringBuilder(value);
  				}
  			}
  		}
  
  		public string EncodedText {
  			get { return encoded_text; }
  		}
  
  		public Picture Picture {
  			get { return picture; }
  			set { picture = value; }
  		}
  
  		public Color Colors {
  			get {
  				return colors;
  			}
  
  			set {
  				colors = value;
  			}
  		}
  
  		public Style Styles {
  			get {
  				return styles;
  			}
  
  			set {
  				styles = value;
  			}
  		}
  
  		public Font Fonts {
  			get {
  				return fonts;
  			}
  
  			set {
  				fonts = value;
  			}
  		}
  
  		public ClassCallback ClassCallback {
  			get {
  				return class_callbacks;
  			}
  
  			set {
  				class_callbacks = value;
  			}
  		}
  
  		public DestinationCallback DestinationCallback {
  			get {
  				return destination_callbacks;
  			}
  
  			set {
  				destination_callbacks = value;
  			}
  		}
  
  		public int LineNumber {
  			get {
  				return line_num;
  			}
  		}
  
  		public int LinePos {
  			get {
  				return line_pos;
  			}
  		}
  		#endregion	// Properties
  
  		#region Methods
  		/// <summary>Set the default font for documents without font table</summary>
  		public void DefaultFont(string name) {
  			Font font;
  
  			font = new Font(this);
  			font.Num = 0;
  			font.Name = name;
  		}
  
  		/// <summary>Read the next character from the input - skip any crlf</summary>
  		private char GetChar ()
  		{
  			return GetChar (true);
  		}
  
  		/// <summary>Read the next character from the input</summary>
  		private char GetChar(bool skipCrLf) 
  		{
  			int	c;
  			bool	old_bump_line;
  
  SkipCRLF:
  			if ((c = source.Read()) != -1) {
  				this.text_buffer.Append((char) c);
  			}
  
  			if (this.prev_char == EOF) {
  				this.bump_line = true;
  			}
  
  			old_bump_line = bump_line;
  			bump_line = false;
  
  			if (skipCrLf) {
  				if (c == '\r') {
  					bump_line = true;
  					text_buffer.Length--;
  					goto SkipCRLF;
  				} else if (c == '\n') {
  					bump_line = true;
  					if (this.prev_char == '\r') {
  						old_bump_line = false;
  					}
  
  					text_buffer.Length--;
  					goto SkipCRLF;
  				}
  			}
  
  			this.line_pos ++;
  			if (old_bump_line) {
  				this.line_num++;
  				this.line_pos = 1;
  			}
  
  			this.prev_char = (char) c;
  			return (char) c;
  		}
  
  		/// <summary>Parse the RTF stream</summary>
  		public void Read() {
  			while (GetToken() != TokenClass.EOF) {
  				RouteToken();
  			}
  		}
  
  		/// <summary>Route a token</summary>
  		public void RouteToken() {
  
  			if (CheckCM(TokenClass.Control, Major.Destination)) {
  				DestinationDelegate d;
  
  				d = destination_callbacks[minor];
  				if (d != null) {
  					d(this);
  				}
  			}
  
  			// Invoke class callback if there is one
  			ClassDelegate c;
  
  			c = class_callbacks[rtf_class];
  
gt: 			if (c != null) {