1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //	Peter Bartok	(pbartok@novell.com)
24 //
25 
26 // COMPLETE
27 
28 #undef RTF_DEBUG
29 
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Text;
34 
35 namespace System.Windows.Forms.RTF {
36 	internal class RTF {
37 		#region	Local Variables
38 		internal const char	EOF = unchecked((char)-1);
39 		internal const int	NoParam = -1000000;
40 		internal const int	DefaultEncodingCodePage = 1252;
41 
42 		private TokenClass	rtf_class;
43 		private Major		major;
44 		private Minor		minor;
45 		private int		param;
46 		private string encoded_text;
47 		private Encoding encoding;
48 		private int encoding_code_page = DefaultEncodingCodePage;
49 		private StringBuilder	text_buffer;
50 		private Picture picture;
51 		private int		line_num;
52 		private int		line_pos;
53 
54 		private char		pushed_char;
55 		//private StringBuilder	pushed_text_buffer;
56 		private TokenClass	pushed_class;
57 		private Major		pushed_major;
58 		private Minor		pushed_minor;
59 		private int		pushed_param;
60 
61 		private char		prev_char;
62 		private bool		bump_line;
63 
64 		private Font		font_list;
65 
66 		private Charset		cur_charset;
67 		private Stack		charset_stack;
68 
69 		private Style		styles;
70 		private Color		colors;
71 		private Font		fonts;
72 
73 		private StreamReader	source;
74 
75 		private static Hashtable	key_table;
76 		private static KeyStruct[]	Keys = KeysInit.Init();
77 
78 		private DestinationCallback	destination_callbacks;
79 		private ClassCallback		class_callbacks;
80 		#endregion	// Local Variables
81 
82 		#region Constructors
RTF()83 		static RTF() {
84 			key_table = new Hashtable(Keys.Length);
85 			for (int i = 0; i < Keys.Length; i++) {
86 				key_table[Keys[i].Symbol] = Keys[i];
87 			}
88 		}
89 
RTF(Stream stream)90 		public RTF(Stream stream) {
91 			source = new StreamReader(stream);
92 
93 			text_buffer = new StringBuilder(1024);
94 			//pushed_text_buffer = new StringBuilder(1024);
95 
96 			rtf_class = TokenClass.None;
97 			pushed_class = TokenClass.None;
98 			pushed_char = unchecked((char)-1);
99 
100 			line_num = 0;
101 			line_pos = 0;
102 			prev_char = unchecked((char)-1);
103 			bump_line = false;
104 			font_list = null;
105 			charset_stack = null;
106 
107 			cur_charset = new Charset();
108 
109 			destination_callbacks = new DestinationCallback();
110 			class_callbacks = new ClassCallback();
111 
112 			destination_callbacks [Minor.OptDest] = new DestinationDelegate (HandleOptDest);
113 			destination_callbacks[Minor.FontTbl] = new DestinationDelegate(ReadFontTbl);
114 			destination_callbacks[Minor.ColorTbl] = new DestinationDelegate(ReadColorTbl);
115 			destination_callbacks[Minor.StyleSheet] = new DestinationDelegate(ReadStyleSheet);
116 			destination_callbacks[Minor.Info] = new DestinationDelegate(ReadInfoGroup);
117 			destination_callbacks[Minor.Pict] = new DestinationDelegate(ReadPictGroup);
118 			destination_callbacks[Minor.Object] = new DestinationDelegate(ReadObjGroup);
119 		}
120 		#endregion	// Constructors
121 
122 		#region Properties
123 		public TokenClass TokenClass {
124 			get {
125 				return this.rtf_class;
126 			}
127 
128 			set {
129 				this.rtf_class = value;
130 			}
131 		}
132 
133 		public Major Major {
134 			get {
135 				return this.major;
136 			}
137 
138 			set {
139 				this.major = value;
140 			}
141 		}
142 
143 		public Minor Minor {
144 			get {
145 				return this.minor;
146 			}
147 
148 			set {
149 				this.minor = value;
150 			}
151 		}
152 
153 		public int Param {
154 			get {
155 				return this.param;
156 			}
157 
158 			set {
159 				this.param = value;
160 			}
161 		}
162 
163 		public string Text {
164 			get {
165 				return this.text_buffer.ToString();
166 			}
167 
168 			set {
169 				if (value == null) {
170 					this.text_buffer.Length = 0;
171 				} else {
172 					this.text_buffer = new StringBuilder(value);
173 				}
174 			}
175 		}
176 
177 		public string EncodedText {
178 			get { return encoded_text; }
179 		}
180 
181 		public Picture Picture {
182 			get { return picture; }
183 			set { picture = value; }
184 		}
185 
186 		public Color Colors {
187 			get {
188 				return colors;
189 			}
190 
191 			set {
192 				colors = value;
193 			}
194 		}
195 
196 		public Style Styles {
197 			get {
198 				return styles;
199 			}
200 
201 			set {
202 				styles = value;
203 			}
204 		}
205 
206 		public Font Fonts {
207 			get {
208 				return fonts;
209 			}
210 
211 			set {
212 				fonts = value;
213 			}
214 		}
215 
216 		public ClassCallback ClassCallback {
217 			get {
218 				return class_callbacks;
219 			}
220 
221 			set {
222 				class_callbacks = value;
223 			}
224 		}
225 
226 		public DestinationCallback DestinationCallback {
227 			get {
228 				return destination_callbacks;
229 			}
230 
231 			set {
232 				destination_callbacks = value;
233 			}
234 		}
235 
236 		public int LineNumber {
237 			get {
238 				return line_num;
239 			}
240 		}
241 
242 		public int LinePos {
243 			get {
244 				return line_pos;
245 			}
246 		}
247 		#endregion	// Properties
248 
249 		#region Methods
250 		/// <summary>Set the default font for documents without font table</summary>
DefaultFont(string name)251 		public void DefaultFont(string name) {
252 			Font font;
253 
254 			font = new Font(this);
255 			font.Num = 0;
256 			font.Name = name;
257 		}
258 
259 		/// <summary>Read the next character from the input - skip any crlf</summary>
GetChar()260 		private char GetChar ()
261 		{
262 			return GetChar (true);
263 		}
264 
265 		/// <summary>Read the next character from the input</summary>
GetChar(bool skipCrLf)266 		private char GetChar(bool skipCrLf)
267 		{
268 			int	c;
269 			bool	old_bump_line;
270 
271 SkipCRLF:
272 			if ((c = source.Read()) != -1) {
273 				this.text_buffer.Append((char) c);
274 			}
275 
276 			if (this.prev_char == EOF) {
277 				this.bump_line = true;
278 			}
279 
280 			old_bump_line = bump_line;
281 			bump_line = false;
282 
283 			if (skipCrLf) {
284 				if (c == '\r') {
285 					bump_line = true;
286 					text_buffer.Length--;
287 					goto SkipCRLF;
288 				} else if (c == '\n') {
289 					bump_line = true;
290 					if (this.prev_char == '\r') {
291 						old_bump_line = false;
292 					}
293 
294 					text_buffer.Length--;
295 					goto SkipCRLF;
296 				}
297 			}
298 
299 			this.line_pos ++;
300 			if (old_bump_line) {
301 				this.line_num++;
302 				this.line_pos = 1;
303 			}
304 
305 			this.prev_char = (char) c;
306 			return (char) c;
307 		}
308 
309 		/// <summary>Parse the RTF stream</summary>
Read()310 		public void Read() {
311 			while (GetToken() != TokenClass.EOF) {
312 				RouteToken();
313 			}
314 		}
315 
316 		/// <summary>Route a token</summary>
RouteToken()317 		public void RouteToken() {
318 
319 			if (CheckCM(TokenClass.Control, Major.Destination)) {
320 				DestinationDelegate d;
321 
322 				d = destination_callbacks[minor];
323 				if (d != null) {
324 					d(this);
325 				}
326 			}
327 
328 			// Invoke class callback if there is one
329 			ClassDelegate c;
330 
331 			c = class_callbacks[rtf_class];
332 			if (c != null) {
333 				c(this);
334 			}
335 
336 		}
337 
338 		/// <summary>Skip to the end of the next group to start or current group we are in</summary>
SkipGroup()339 		public void SkipGroup() {
340 			int	level;
341 
342 			level = 1;
343 
344 			while (GetToken() != TokenClass.EOF) {
345 				if (rtf_class == TokenClass.Group) {
346 					if (this.major == Major.BeginGroup) {
347 						level++;
348 					} else if (this.major == Major.EndGroup) {
349 						level--;
350 						if (level < 1) {
351 							break;
352 						}
353 					}
354 				}
355 			}
356 		}
357 
358 		/// <summary>Return the next token in the stream</summary>
GetToken()359 		public TokenClass GetToken() {
360 			if (pushed_class != TokenClass.None) {
361 				this.rtf_class = this.pushed_class;
362 				this.major = this.pushed_major;
363 				this.minor = this.pushed_minor;
364 				this.param = this.pushed_param;
365 				this.pushed_class = TokenClass.None;
366 				return this.rtf_class;
367 			}
368 
369 			GetToken2();
370 
371 			if (this.rtf_class == TokenClass.Text) {
372 				this.minor = (Minor)this.cur_charset[(int)this.major];
373 				if (encoding == null) {
374 					encoding = Encoding.GetEncoding (encoding_code_page);
375 				}
376 				encoded_text = new String (encoding.GetChars (new byte [] { (byte) this.major }));
377 			}
378 
379 			if (this.cur_charset.Flags == CharsetFlags.None) {
380 				return this.rtf_class;
381 			}
382 
383 			if (CheckCMM (TokenClass.Control, Major.Unicode, Minor.UnicodeAnsiCodepage)) {
384 				encoding_code_page = param;
385 
386 				// fallback to the default one in case we have an invalid value
387 				if (encoding_code_page < 0 || encoding_code_page > 65535)
388 					encoding_code_page = DefaultEncodingCodePage;
389 			}
390 
391 			if (((this.cur_charset.Flags & CharsetFlags.Read) != 0) && CheckCM(TokenClass.Control, Major.CharSet)) {
392 				this.cur_charset.ReadMap();
393 			} else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
394 				Font	fp;
395 
396 				fp = Font.GetFont(this.font_list, this.param);
397 
398 				if (fp != null) {
399 					if (fp.Name.StartsWith("Symbol")) {
400 						this.cur_charset.ID = CharsetType.Symbol;
401 					} else {
402 						this.cur_charset.ID = CharsetType.General;
403 					}
404 				} else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) {
405 					switch(this.major) {
406 						case Major.BeginGroup: {
407 							this.charset_stack.Push(this.cur_charset);
408 							break;
409 						}
410 
411 						case Major.EndGroup: {
412 							this.cur_charset = (Charset)this.charset_stack.Pop();
413 							break;
414 						}
415 					}
416 				}
417 			}
418 
419 			return this.rtf_class;
420 		}
421 
GetToken2()422 		private void GetToken2() {
423 			char	c;
424 			int	sign;
425 
426 			this.rtf_class = TokenClass.Unknown;
427 			this.param = NoParam;
428 
429 			this.text_buffer.Length = 0;
430 
431 			if (this.pushed_char != EOF) {
432 				c = this.pushed_char;
433 				this.text_buffer.Append(c);
434 				this.pushed_char = EOF;
435 			} else if ((c = GetChar()) == EOF) {
436 				this.rtf_class = TokenClass.EOF;
437 				return;
438 			}
439 
440 			if (c == '{') {
441 				this.rtf_class = TokenClass.Group;
442 				this.major = Major.BeginGroup;
443 				return;
444 			}
445 
446 			if (c == '}') {
447 				this.rtf_class = TokenClass.Group;
448 				this.major = Major.EndGroup;
449 				return;
450 			}
451 
452 			if (c != '\\') {
453 				if (c != '\t') {
454 					this.rtf_class = TokenClass.Text;
455 					this.major = (Major)c;	// FIXME - typing?
456 					return;
457 				} else {
458 					this.rtf_class = TokenClass.Control;
459 					this.major = Major.SpecialChar;
460 					this.minor = Minor.Tab;
461 					return;
462 				}
463 			}
464 
465 			if ((c = GetChar()) == EOF) {
466 				// Not so good
467 				return;
468 			}
469 
470 			if (!Char.IsLetter(c)) {
471 				if (c == '\'') {
472 					char c2;
473 
474 					if ((c = GetChar()) == EOF) {
475 						return;
476 					}
477 
478 					if ((c2 = GetChar()) == EOF) {
479 						return;
480 					}
481 
482 					this.rtf_class = TokenClass.Text;
483 					this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16))));
484 					return;
485 				}
486 
487 				// Escaped char
488 				if (c == ':' || c == '{' || c == '}' || c == '\\') {
489 					this.rtf_class = TokenClass.Text;
490 					this.major = (Major)c;
491 					return;
492 				}
493 
494 				Lookup(this.text_buffer.ToString());
495 				return;
496 			}
497 
498 			while (Char.IsLetter(c)) {
499 				if ((c = GetChar(false)) == EOF) {
500 					break;
501 				}
502 			}
503 
504 			if (c != EOF) {
505 				this.text_buffer.Length--;
506 			}
507 
508 			Lookup(this.text_buffer.ToString());
509 
510 			if (c != EOF) {
511 				this.text_buffer.Append(c);
512 			}
513 
514 			sign = 1;
515 			if (c == '-') {
516 				sign = -1;
517 				c = GetChar();
518 			}
519 
520 			if (c != EOF && Char.IsDigit(c) && minor != Minor.PngBlip) {
521 				this.param = 0;
522 				while (Char.IsDigit(c)) {
523 					this.param = this.param * 10 + Convert.ToByte(c) - 48;
524 					if ((c = GetChar()) == EOF) {
525 						break;
526 					}
527 				}
528 				this.param *= sign;
529 			}
530 
531 			if (c != EOF) {
532 				if (c != ' ' && c != '\r' && c != '\n') {
533 					this.pushed_char = c;
534 				}
535 				this.text_buffer.Length--;
536 			}
537 		}
538 
SetToken(TokenClass cl, Major maj, Minor min, int par, string text)539 		public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) {
540 			this.rtf_class = cl;
541 			this.major = maj;
542 			this.minor = min;
543 			this.param = par;
544 			if (par == NoParam) {
545 				this.text_buffer = new StringBuilder(text);
546 			} else {
547 				this.text_buffer = new StringBuilder(text + par.ToString());
548 			}
549 		}
550 
UngetToken()551 		public void UngetToken() {
552 			if (this.pushed_class != TokenClass.None) {
553 				throw new RTFException(this, "Cannot unget more than one token");
554 			}
555 
556 			if (this.rtf_class == TokenClass.None) {
557 				throw new RTFException(this, "No token to unget");
558 			}
559 
560 			this.pushed_class = this.rtf_class;
561 			this.pushed_major = this.major;
562 			this.pushed_minor = this.minor;
563 			this.pushed_param = this.param;
564 			//this.pushed_text_buffer = new StringBuilder(this.text_buffer.ToString());
565 		}
566 
PeekToken()567 		public TokenClass PeekToken() {
568 			GetToken();
569 			UngetToken();
570 			return rtf_class;
571 		}
572 
Lookup(string token)573 		public void Lookup(string token) {
574 			Object		obj;
575 			KeyStruct	key;
576 
577 			obj = key_table[token.Substring(1)];
578 			if (obj == null) {
579 				rtf_class = TokenClass.Unknown;
580 				major = (Major) -1;
581 				minor = (Minor) -1;
582 				return;
583 			}
584 
585 			key = (KeyStruct)obj;
586 			this.rtf_class = TokenClass.Control;
587 			this.major = key.Major;
588 			this.minor = key.Minor;
589 		}
590 
CheckCM(TokenClass rtf_class, Major major)591 		public bool CheckCM(TokenClass rtf_class, Major major) {
592 			if ((this.rtf_class == rtf_class) && (this.major == major)) {
593 				return true;
594 			}
595 
596 			return false;
597 		}
598 
CheckCMM(TokenClass rtf_class, Major major, Minor minor)599 		public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) {
600 			if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) {
601 				return true;
602 			}
603 
604 			return false;
605 		}
606 
CheckMM(Major major, Minor minor)607 		public bool CheckMM(Major major, Minor minor) {
608 			if ((this.major == major) && (this.minor == minor)) {
609 				return true;
610 			}
611 
612 			return false;
613 		}
614 		#endregion	// Methods
615 
616 		#region Default Delegates
617 
HandleOptDest(RTF rtf)618 		private void HandleOptDest (RTF rtf)
619 		{
620 			int group_levels = 1;
621 
622 			while (true) {
623 				GetToken ();
624 
625 				// Here is where we should handle recognised optional
626 				// destinations.
627 				//
628 				// Handle a picture group
629 				//
630 				if (rtf.CheckCMM (TokenClass.Control, Major.Destination, Minor.Pict)) {
631 					ReadPictGroup (rtf);
632 					return;
633 				}
634 
635 				if (rtf.CheckCM (TokenClass.Group, Major.EndGroup)) {
636 					if ((--group_levels) == 0) {
637 						break;
638 					}
639 				}
640 
641 				if (rtf.CheckCM (TokenClass.Group, Major.BeginGroup)) {
642 					group_levels++;
643 				}
644 			}
645 		}
646 
ReadFontTbl(RTF rtf)647 		private void ReadFontTbl(RTF rtf) {
648 			int	old;
649 			Font	font;
650 
651 			old = -1;
652 			font = null;
653 
654 			while (true) {
655 				rtf.GetToken();
656 
657 				if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
658 					break;
659 				}
660 
661 				if (old < 0) {
662 					if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
663 						old = 1;
664 					} else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
665 						old = 0;
666 					} else {
667 						throw new RTFException(rtf, "Cannot determine format");
668 					}
669 				}
670 
671 				if (old == 0) {
672 					if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
673 						throw new RTFException(rtf, "missing \"{\"");
674 					}
675 					rtf.GetToken();
676 				}
677 
678 				font = new Font(rtf);
679 
680 				while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
681 					if (rtf.rtf_class == TokenClass.Control) {
682 						switch(rtf.major) {
683 							case Major.FontFamily: {
684 								font.Family = (int)rtf.minor;
685 								break;
686 							}
687 
688 							case Major.CharAttr: {
689 								switch(rtf.minor) {
690 									case Minor.FontNum: {
691 										font.Num = rtf.param;
692 										break;
693 									}
694 
695 									default: {
696 										#if RTF_DEBUG
697 											Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor);
698 										#endif
699 										break;
700 									}
701 								}
702 								break;
703 							}
704 
705 							case Major.FontAttr: {
706 								switch (rtf.minor) {
707 									case Minor.FontCharSet: {
708 										font.Charset = (CharsetType)rtf.param;
709 										break;
710 									}
711 
712 									case Minor.FontPitch: {
713 										font.Pitch = rtf.param;
714 										break;
715 									}
716 
717 									case Minor.FontCodePage: {
718 										font.Codepage = rtf.param;
719 										break;
720 									}
721 
722 									case Minor.FTypeNil:
723 									case Minor.FTypeTrueType: {
724 										font.Type = rtf.param;
725 										break;
726 									}
727 									default: {
728 										#if RTF_DEBUG
729 											Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor);
730 										#endif
731 										break;
732 									}
733 								}
734 								break;
735 							}
736 
737 							default: {
738 								#if RTF_DEBUG
739 									Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major);
740 								#endif
741 								break;
742 							}
743 						}
744 					} else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
745 						rtf.SkipGroup();
746 					} else if (rtf.rtf_class == TokenClass.Text) {
747 						StringBuilder	sb;
748 
749 						sb = new StringBuilder();
750 
751 						while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) && (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup))) {
752 							sb.Append((char)rtf.major);
753 							rtf.GetToken();
754 						}
755 
756 						if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
757 							rtf.UngetToken();
758 						}
759 
760 						font.Name = sb.ToString();
761 						continue;
762 #if RTF_DEBUG
763 					} else {
764 						Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer);
765 #endif
766 					}
767 
768 					rtf.GetToken();
769 				}
770 
771 				if (old == 0) {
772 					rtf.GetToken();
773 
774 					if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
775 						throw new RTFException(rtf, "Missing \"}\"");
776 					}
777 				}
778 			}
779 
780 			if (font == null) {
781 				throw new RTFException(rtf, "No font created");
782 			}
783 
784 			if (font.Num == -1) {
785 				throw new RTFException(rtf, "Missing font number");
786 			}
787 
788 			rtf.RouteToken();
789 		}
790 
ReadColorTbl(RTF rtf)791 		private void ReadColorTbl(RTF rtf) {
792 			Color	color;
793 			int	num;
794 
795 			num = 0;
796 
797 			while (true) {
798 				rtf.GetToken();
799 
800 				if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
801 					break;
802 				}
803 
804 				color = new Color(rtf);
805 				color.Num = num++;
806 
807 				while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) {
808 					switch (rtf.minor) {
809 						case Minor.Red: {
810 							color.Red = rtf.param;
811 							break;
812 						}
813 
814 						case Minor.Green: {
815 							color.Green = rtf.param;
816 							break;
817 						}
818 
819 						case Minor.Blue: {
820 							color.Blue = rtf.param;
821 							break;
822 						}
823 					}
824 
825 					rtf.GetToken();
826 				}
827 				if (!rtf.CheckCM(TokenClass.Text, (Major)';')) {
828 					throw new RTFException(rtf, "Malformed color entry");
829 				}
830 			}
831 			rtf.RouteToken();
832 		}
833 
ReadStyleSheet(RTF rtf)834 		private void ReadStyleSheet(RTF rtf) {
835 			Style		style;
836 			StringBuilder	sb;
837 
838 			sb = new StringBuilder();
839 
840 			while (true) {
841 				rtf.GetToken();
842 
843 				if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
844 					break;
845 				}
846 
847 				style = new Style(rtf);
848 
849 				if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
850 					throw new RTFException(rtf, "Missing \"{\"");
851 				}
852 
853 				while (true) {
854 					rtf.GetToken();
855 
856 					if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) {
857 						break;
858 					}
859 
860 					if (rtf.rtf_class == TokenClass.Control) {
861 						if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) {
862 							style.Num = rtf.param;
863 							style.Type = StyleType.Paragraph;
864 							continue;
865 						}
866 						if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) {
867 							style.Num = rtf.param;
868 							style.Type = StyleType.Character;
869 							continue;
870 						}
871 						if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) {
872 							style.Num = rtf.param;
873 							style.Type = StyleType.Section;
874 							continue;
875 						}
876 						if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) {
877 							style.BasedOn = rtf.param;
878 							continue;
879 						}
880 						if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) {
881 							style.Additive = true;
882 							continue;
883 						}
884 						if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) {
885 							style.NextPar = rtf.param;
886 							continue;
887 						}
888 
889 						new StyleElement(style, rtf.rtf_class, rtf.major, rtf.minor, rtf.param, rtf.text_buffer.ToString());
890 					} else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
891 						// This passes over "{\*\keycode ... }, among other things
892 						rtf.SkipGroup();
893 					} else if (rtf.rtf_class == TokenClass.Text) {
894 						while (rtf.rtf_class == TokenClass.Text) {
895 							if (rtf.major == (Major)';') {
896 								rtf.UngetToken();
897 								break;
898 							}
899 
900 							sb.Append((char)rtf.major);
901 							rtf.GetToken();
902 						}
903 
904 						style.Name = sb.ToString();
905 #if RTF_DEBUG
906 					} else {
907 						Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer);
908 #endif
909 					}
910 				}
911 				rtf.GetToken();
912 
913 				if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
914 					throw new RTFException(rtf, "Missing EndGroup (\"}\"");
915 				}
916 
917 				// Sanity checks
918 				if (style.Name == null) {
919 					throw new RTFException(rtf, "Style must have name");
920 				}
921 
922 				if (style.Num < 0) {
923 					if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) {
924 						throw new RTFException(rtf, "Missing style number");
925 					}
926 
927 					style.Num = Style.NormalStyleNum;
928 				}
929 
930 				if (style.NextPar == -1) {
931 					style.NextPar = style.Num;
932 				}
933 			}
934 
935 			rtf.RouteToken();
936 		}
937 
ReadInfoGroup(RTF rtf)938 		private void ReadInfoGroup(RTF rtf) {
939 			rtf.SkipGroup();
940 			rtf.RouteToken();
941 		}
942 
ReadPictGroup(RTF rtf)943 		private void ReadPictGroup(RTF rtf)
944 		{
945 			bool read_image_data = false;
946 
947 			Picture picture = new Picture ();
948 			while (true) {
949 				rtf.GetToken ();
950 
951 				if (rtf.CheckCM (TokenClass.Group, Major.EndGroup))
952 					break;
953 
954 				switch (minor) {
955 				case Minor.PngBlip:
956 					picture.ImageType = minor;
957 					read_image_data = true;
958 					break;
959 				case Minor.WinMetafile:
960 					picture.ImageType = minor;
961 					read_image_data = true;
962 					continue;
963 				case Minor.PicWid:
964 					continue;
965 				case Minor.PicHt:
966 					continue;
967 				case Minor.PicGoalWid:
968 					picture.SetWidthFromTwips (param);
969 					continue;
970 				case Minor.PicGoalHt:
971 					picture.SetHeightFromTwips (param);
972 					continue;
973 				}
974 
975 				if (read_image_data && rtf.rtf_class == TokenClass.Text) {
976 
977 					picture.Data.Seek (0, SeekOrigin.Begin);
978 
979 					//char c = (char) rtf.major;
980 
981 					uint digitValue1;
982 					uint digitValue2;
983 					char hexDigit1 = (char) rtf.major;
984 					char hexDigit2;
985 
986 					while (true) {
987 
988 						while (hexDigit1 == '\n' || hexDigit1 == '\r') {
989 							hexDigit1 = (char) source.Peek ();
990 							if (hexDigit1 == '}')
991 								break;
992 							hexDigit1 = (char) source.Read ();
993 						}
994 
995 						hexDigit2 = (char) source.Peek ();
996 						if (hexDigit2 == '}')
997 							break;
998 						hexDigit2 = (char) source.Read ();
999 						while (hexDigit2 == '\n' || hexDigit2 == '\r') {
1000 							hexDigit2 = (char) source.Peek ();
1001 							if (hexDigit2 == '}')
1002 								break;
1003 							hexDigit2 = (char) source.Read ();
1004 						}
1005 
1006 						if (Char.IsDigit (hexDigit1))
1007 							digitValue1 = (uint) (hexDigit1 - '0');
1008 						else if (Char.IsLower (hexDigit1))
1009 							digitValue1 = (uint) (hexDigit1 - 'a' + 10);
1010 						else if (Char.IsUpper (hexDigit1))
1011 							digitValue1 = (uint) (hexDigit1 - 'A' + 10);
1012 						else if (hexDigit1 == '\n' || hexDigit1 == '\r')
1013 							continue;
1014 						else
1015 							break;
1016 
1017 						if (Char.IsDigit (hexDigit2))
1018 							digitValue2 = (uint) (hexDigit2 - '0');
1019 						else if (Char.IsLower (hexDigit2))
1020 							digitValue2 = (uint) (hexDigit2 - 'a' + 10);
1021 						else if (Char.IsUpper (hexDigit2))
1022 							digitValue2 = (uint) (hexDigit2 - 'A' + 10);
1023 						else if (hexDigit2 == '\n' || hexDigit2 == '\r')
1024 							continue;
1025 						else
1026 							break;
1027 
1028 						picture.Data.WriteByte ((byte) checked (digitValue1 * 16 + digitValue2));
1029 
1030 						// We get the first hex digit at the end, since in the very first
1031 						// iteration we use rtf.major as the first hex digit
1032 						hexDigit1 = (char) source.Peek ();
1033 						if (hexDigit1 == '}')
1034 							break;
1035 						hexDigit1 = (char) source.Read ();
1036 					}
1037 
1038 
1039 					read_image_data = false;
1040 					break;
1041 				}
1042 			}
1043 
1044 			if (picture.ImageType != Minor.Undefined && !read_image_data) {
1045 				this.picture = picture;
1046 				SetToken (TokenClass.Control, Major.PictAttr, picture.ImageType, 0, String.Empty);
1047 			}
1048 		}
1049 
ReadObjGroup(RTF rtf)1050 		private void ReadObjGroup(RTF rtf) {
1051 			rtf.SkipGroup();
1052 			rtf.RouteToken();
1053 		}
1054 		#endregion	// Default Delegates
1055 	}
1056 }
1057