* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms.RTF / RTF.cs
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
41                 private TokenClass      rtf_class;
42                 private Major           major;
43                 private Minor           minor;
44                 private int             param;
45                 private int             format;
46                 private StringBuilder   text_buffer;
47                 private int             line_num;
48                 private int             line_pos;
49
50                 private char            pushed_char;
51                 private StringBuilder   pushed_text_buffer;
52                 private TokenClass      pushed_class;
53                 private Major           pushed_major;
54                 private Minor           pushed_minor;
55                 private int             pushed_param;
56
57                 private char            prev_char;
58                 private bool            bump_line;
59
60                 private Font            font_list;
61                 private Color           color_list;
62                 private Style           style_list;
63
64                 private Charset         cur_charset;
65                 private Stack           charset_stack;
66
67                 private Style           styles;
68                 private Color           colors;
69                 private Font            fonts;
70
71                 private StreamReader    source;
72
73                 private static Hashtable        key_table;
74                 private static KeyStruct[]      Keys = KeysInit.Init();
75
76                 private DestinationCallback     destination_callbacks;
77                 private ClassCallback           class_callbacks;
78                 #endregion      // Local Variables
79
80                 #region Constructors
81                 static RTF() {
82                         key_table = new Hashtable(Keys.Length);
83                         for (int i = 0; i < Keys.Length; i++) {
84                                 key_table[Keys[i].Symbol] = Keys[i];
85                         }
86                 }
87
88                 public RTF(Stream stream) {
89                         source = new StreamReader(stream);
90
91                         text_buffer = new StringBuilder(1024);
92                         pushed_text_buffer = new StringBuilder(1024);
93
94                         rtf_class = TokenClass.None;
95                         pushed_class = TokenClass.None;
96                         pushed_char = unchecked((char)-1);
97
98                         line_num = 0;
99                         line_pos = 0;
100                         prev_char = unchecked((char)-1);
101                         bump_line = false;
102
103                         cur_charset = new Charset();
104
105                         destination_callbacks = new DestinationCallback();
106                         class_callbacks = new ClassCallback();
107
108                         destination_callbacks[Minor.FontTbl] = new DestinationDelegate(ReadFontTbl);
109                         destination_callbacks[Minor.ColorTbl] = new DestinationDelegate(ReadColorTbl);
110                         destination_callbacks[Minor.StyleSheet] = new DestinationDelegate(ReadStyleSheet);
111                         destination_callbacks[Minor.Info] = new DestinationDelegate(ReadInfoGroup);
112                         destination_callbacks[Minor.Pict] = new DestinationDelegate(ReadPictGroup);
113                         destination_callbacks[Minor.Object] = new DestinationDelegate(ReadObjGroup);
114                 }
115                 #endregion      // Constructors
116
117                 #region Properties
118                 public TokenClass TokenClass {
119                         get {
120                                 return this.rtf_class;
121                         }
122
123                         set {
124                                 this.rtf_class = value;
125                         }
126                 }
127
128                 public Major Major {
129                         get {
130                                 return this.major;
131                         }
132
133                         set {
134                                 this.major = value;
135                         }
136                 }
137
138                 public Minor Minor {
139                         get {
140                                 return this.minor;
141                         }
142
143                         set {
144                                 this.minor = value;
145                         }
146                 }
147
148                 public int Param {
149                         get {
150                                 return this.param;
151                         }
152
153                         set {
154                                 this.param = value;
155                         }
156                 }
157
158                 public string Text {
159                         get {
160                                 return this.text_buffer.ToString();
161                         }
162
163                         set {
164                                 if (value == null) {
165                                         this.text_buffer.Length = 0;
166                                 } else {
167                                         this.text_buffer = new StringBuilder(value);
168                                 }
169                         }
170                 }
171
172                 public Color Colors {
173                         get {
174                                 return colors;
175                         }
176
177                         set {
178                                 colors = value;
179                         }
180                 }
181
182                 public Style Styles {
183                         get {
184                                 return styles;
185                         }
186
187                         set {
188                                 styles = value;
189                         }
190                 }
191
192                 public Font Fonts {
193                         get {
194                                 return fonts;
195                         }
196
197                         set {
198                                 fonts = value;
199                         }
200                 }
201
202                 public ClassCallback ClassCallback {
203                         get {
204                                 return class_callbacks;
205                         }
206
207                         set {
208                                 class_callbacks = value;
209                         }
210                 }
211
212                 public DestinationCallback DestinationCallback {
213                         get {
214                                 return destination_callbacks;
215                         }
216
217                         set {
218                                 destination_callbacks = value;
219                         }
220                 }
221
222                 public int LineNumber {
223                         get {
224                                 return line_num;
225                         }
226                 }
227
228                 public int LinePos {
229                         get {
230                                 return line_pos;
231                         }
232                 }
233                 #endregion      // Properties
234
235                 #region Methods
236                 /// <summary>Set the default font for documents without font table</summary>
237                 public void DefaultFont(string name) {
238                         Font font;
239
240                         font = new Font(this);
241                         font.Num = 0;
242                         font.Name = name;
243                 }
244
245                 /// <summary>Read the next character from the input</summary>
246                 private char GetChar() {
247                         char    c;
248                         bool    old_bump_line;
249
250 SkipCRLF:
251                         if ((c = (char)source.Read()) != EOF) {
252                                 this.text_buffer.Append(c);
253                         }
254
255                         if (this.prev_char == EOF) {
256                                 this.bump_line = true;
257                         }
258
259                         old_bump_line = bump_line;
260                         bump_line = false;
261
262                         if (c == '\r') {
263                                 bump_line = true;
264                                 text_buffer.Length--;
265                                 goto SkipCRLF;
266                         } else if (c == '\n') {
267                                 bump_line = true;
268                                 if (this.prev_char == '\r') {
269                                         old_bump_line = false;
270                                 }
271                                 text_buffer.Length--;
272                                 goto SkipCRLF;
273                         }
274
275                         this.line_pos ++;
276                         if (old_bump_line) {
277                                 this.line_num++;
278                                 this.line_pos = 1;
279                         }
280
281                         this.prev_char = c;
282                         return c;
283                 }
284
285                 /// <summary>Parse the RTF stream</summary>
286                 public void Read() {
287                         while (GetToken() != TokenClass.EOF) {
288                                 RouteToken();
289                         }
290                 }
291
292                 /// <summary>Route a token</summary>
293                 public void RouteToken() {
294                         if (CheckCM(TokenClass.Control, Major.Destination)) {
295                                 DestinationDelegate d;
296
297                                 d = destination_callbacks[minor];
298                                 if (d != null) {
299                                         d(this);
300                                 }
301                         }
302
303                         // Invoke class callback if there is one
304                         ClassDelegate c;
305
306                         c = class_callbacks[rtf_class];
307                         if (c != null) {
308                                 c(this);
309                         }
310                         
311                 }
312
313                 /// <summary>Skip to the end of the current group</summary>
314                 public void SkipGroup() {
315                         int     level;
316
317                         level = 1;
318
319                         while (GetToken() != TokenClass.EOF) {
320                                 if (rtf_class == TokenClass.Group) {
321                                         if (this.major == Major.BeginGroup) {
322                                                 level++;
323                                         } else if (this.major == Major.EndGroup) {
324                                                 level--;
325                                                 if (level < 1) {
326                                                         break;
327                                                 }
328                                         }
329                                 }
330                         }
331                 }
332
333                 /// <summary>Return the next token in the stream</summary>
334                 public TokenClass GetToken() {
335                         if (pushed_class != TokenClass.None) {
336                                 this.rtf_class = this.pushed_class;
337                                 this.major = this.pushed_major;
338                                 this.minor = this.pushed_minor;
339                                 this.param = this.pushed_param;
340                                 this.pushed_class = TokenClass.None;
341                                 return this.rtf_class;
342                         }
343
344                         GetToken2();
345
346                         if (this.rtf_class == TokenClass.Text) {
347                                 this.minor = (Minor)this.cur_charset[(int)this.major];
348                         }
349
350                         if (this.cur_charset.Flags == CharsetFlags.None) {
351                                 return this.rtf_class;
352                         }
353
354                         if (((this.cur_charset.Flags & CharsetFlags.Read) != 0) && CheckCM(TokenClass.Control, Major.CharSet)) {
355                                 this.cur_charset.ReadMap();
356                         } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
357                                 Font    fp;
358
359                                 fp = Font.GetFont(this.font_list, this.param);
360
361                                 if (fp != null) {
362                                         if (fp.Name.StartsWith("Symbol")) {
363                                                 this.cur_charset.ID = CharsetType.Symbol;
364                                         } else {
365                                                 this.cur_charset.ID = CharsetType.General;
366                                         }
367                                 } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) {
368                                         switch(this.major) {
369                                                 case Major.BeginGroup: {
370                                                         this.charset_stack.Push(this.cur_charset);
371                                                         break;
372                                                 }
373
374                                                 case Major.EndGroup: {
375                                                         this.cur_charset = (Charset)this.charset_stack.Pop();
376                                                         break;
377                                                 }
378                                         }
379                                 }
380                         }
381
382                         return this.rtf_class;
383                 }
384
385                 private void GetToken2() {
386                         char    c;
387                         int     sign;
388
389                         this.rtf_class = TokenClass.Unknown;
390                         this.param = NoParam;
391
392                         this.text_buffer.Length = 0;
393
394                         if (this.pushed_char != EOF) {
395                                 c = this.pushed_char;
396                                 this.text_buffer.Append(c);
397                                 this.pushed_char = EOF;
398                         } else if ((c = GetChar()) == EOF) {
399                                 this.rtf_class = TokenClass.EOF;
400                                 return;
401                         }
402
403                         if (c == '{') {
404                                 this.rtf_class = TokenClass.Group;
405                                 this.major = Major.BeginGroup;
406                                 return;
407                         }
408
409                         if (c == '}') {
410                                 this.rtf_class = TokenClass.Group;
411                                 this.major = Major.EndGroup;
412                                 return;
413                         }
414
415                         if (c != '\\') {
416                                 if (c != '\t') {
417                                         this.rtf_class = TokenClass.Text;
418                                         this.major = (Major)c;  // FIXME - typing?
419                                         return;
420                                 } else {
421                                         this.rtf_class = TokenClass.Control;
422                                         this.major = Major.SpecialChar;
423                                         this.minor = Minor.Tab;
424                                         return;
425                                 }
426                         }
427
428                         if ((c = GetChar()) == EOF) {
429                                 // Not so good
430                                 return;
431                         }
432
433                         if (!Char.IsLetter(c)) {
434                                 if (c == '\'') {
435                                         char c2;
436
437                                         if ((c = GetChar()) == EOF) {
438                                                 return;
439                                         }
440
441                                         if ((c2 = GetChar()) == EOF) {
442                                                 return;
443                                         }
444
445                                         this.rtf_class = TokenClass.Text;
446                                         this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16))));
447                                         return;
448                                 }
449
450                                 // Escaped char
451                                 if (c == ':' || c == '{' || c == '}' || c == '\\') {
452                                         this.rtf_class = TokenClass.Text;
453                                         this.major = (Major)c;
454                                         return;
455                                 }
456
457                                 Lookup(this.text_buffer.ToString());
458                                 return;
459                         }
460
461                         while (Char.IsLetter(c)) {
462                                 if ((c = GetChar()) == EOF) {
463                                         break;
464                                 }
465                         }
466
467                         if (c != EOF) {
468                                 this.text_buffer.Length--;
469                         }
470
471                         Lookup(this.text_buffer.ToString());
472
473                         if (c != EOF) {
474                                 this.text_buffer.Append(c);
475                         }
476
477                         sign = 1;
478                         if (c == '-') {
479                                 sign = -1;
480                                 c = GetChar();
481                         }
482
483                         if (c != EOF && Char.IsDigit(c)) {
484                                 this.param = 0;
485                                 while (Char.IsDigit(c)) {
486                                         this.param = this.param * 10 + Convert.ToByte(c) - 48;
487                                         if ((c = GetChar()) == EOF) {
488                                                 break;
489                                         }
490                                 }
491                                 this.param *= sign;
492                         }
493
494                         if (c != EOF) {
495                                 if (c != ' ') {
496                                         this.pushed_char = c;
497                                 }
498                                 this.text_buffer.Length--;
499                         }
500                 }
501
502                 public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) {
503                         this.rtf_class = cl;
504                         this.major = maj;
505                         this.minor = min;
506                         this.param = par;
507                         if (par == NoParam) {
508                                 this.text_buffer = new StringBuilder(text);
509                         } else {
510                                 this.text_buffer = new StringBuilder(text + par.ToString());
511                         }
512                 }
513
514                 public void UngetToken() {
515                         if (this.pushed_class != TokenClass.None) {
516                                 throw new RTFException(this, "Cannot unget more than one token");
517                         }
518
519                         if (this.rtf_class == TokenClass.None) {
520                                 throw new RTFException(this, "No token to unget");
521                         }
522
523                         this.pushed_class = this.rtf_class;
524                         this.pushed_major = this.major;
525                         this.pushed_minor = this.minor;
526                         this.pushed_param = this.param;
527                         this.pushed_text_buffer = new StringBuilder(this.text_buffer.ToString());
528                 }
529
530                 public TokenClass PeekToken() {
531                         GetToken();
532                         UngetToken();
533                         return rtf_class;
534                 }
535
536                 public void Lookup(string token) {
537                         Object          obj;
538                         KeyStruct       key;
539
540                         obj = key_table[token.Substring(1)];
541                         if (obj == null) {
542                                 rtf_class = TokenClass.Unknown;
543                                 return;
544                         }
545
546                         key = (KeyStruct)obj;
547                         this.rtf_class = TokenClass.Control;
548                         this.major = key.Major;
549                         this.minor = key.Minor;
550                 }
551
552                 public bool CheckCM(TokenClass rtf_class, Major major) {
553                         if ((this.rtf_class == rtf_class) && (this.major == major)) {
554                                 return true;
555                         }
556
557                         return false;
558                 }
559
560                 public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) {
561                         if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) {
562                                 return true;
563                         }
564
565                         return false;
566                 }
567
568                 public bool CheckMM(Major major, Minor minor) {
569                         if ((this.major == major) && (this.minor == minor)) {
570                                 return true;
571                         }
572
573                         return false;
574                 }
575                 #endregion      // Methods
576
577                 #region Default Delegates
578                 private void ReadFontTbl(RTF rtf) {
579                         int     old;
580                         Font    font;
581
582                         old = -1;
583                         font = null;
584
585                         while (true) {
586                                 rtf.GetToken();
587
588                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
589                                         break;
590                                 }
591
592                                 if (old < 0) {
593                                         if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
594                                                 old = 1;
595                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
596                                                 old = 0;
597                                         } else {
598                                                 throw new RTFException(rtf, "Cannot determine format");
599                                         }
600                                 }
601
602                                 if (old == 0) {
603                                         if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
604                                                 throw new RTFException(rtf, "missing \"{\"");
605                                         }
606                                         rtf.GetToken();
607                                 }
608
609                                 font = new Font(rtf);
610
611                                 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
612                                         if (rtf.rtf_class == TokenClass.Control) {
613                                                 switch(rtf.major) {
614                                                         case Major.FontFamily: {
615                                                                 font.Family = (int)rtf.minor;
616                                                                 break;
617                                                         }
618
619                                                         case Major.CharAttr: {
620                                                                 switch(rtf.minor) {
621                                                                         case Minor.FontNum: {
622                                                                                 font.Num = rtf.param;
623                                                                                 break;
624                                                                         }
625
626                                                                         default: {
627                                                                                 #if RTF_DEBUG
628                                                                                         Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor);
629                                                                                 #endif
630                                                                                 break;
631                                                                         }
632                                                                 }
633                                                                 break;
634                                                         }
635
636                                                         case Major.FontAttr: {
637                                                                 switch (rtf.minor) {
638                                                                         case Minor.FontCharSet: {
639                                                                                 font.Charset = (CharsetType)rtf.param;
640                                                                                 break;
641                                                                         }
642
643                                                                         case Minor.FontPitch: {
644                                                                                 font.Pitch = rtf.param;
645                                                                                 break;
646                                                                         }
647
648                                                                         case Minor.FontCodePage: {
649                                                                                 font.Codepage = rtf.param;
650                                                                                 break;
651                                                                         }
652
653                                                                         case Minor.FTypeNil:
654                                                                         case Minor.FTypeTrueType: {
655                                                                                 font.Type = rtf.param;
656                                                                                 break;
657                                                                         }
658                                                                         default: {
659                                                                                 #if RTF_DEBUG
660                                                                                         Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor);
661                                                                                 #endif
662                                                                                 break;
663                                                                         }
664                                                                 }
665                                                                 break;
666                                                         }
667
668                                                         default: {
669                                                                 #if RTF_DEBUG
670                                                                         Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major);
671                                                                 #endif
672                                                                 break;
673                                                         }
674                                                 }
675                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
676                                                 rtf.SkipGroup();
677                                         } else if (rtf.rtf_class == TokenClass.Text) {
678                                                 StringBuilder   sb;
679
680                                                 sb = new StringBuilder();
681
682                                                 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
683                                                         sb.Append((char)rtf.major);
684                                                         rtf.GetToken();
685                                                 }
686
687                                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
688                                                         rtf.UngetToken();
689                                                 }
690
691                                                 font.Name = sb.ToString();
692                                                 continue;
693 #if RTF_DEBUG
694                                         } else {
695                                                 Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer);
696 #endif
697                                         }
698
699                                         rtf.GetToken();
700                                 }
701
702                                 if (old == 0) {
703                                         rtf.GetToken();
704
705                                         if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
706                                                 throw new RTFException(rtf, "Missing \"}\"");
707                                         }
708                                 }
709                         }
710
711                         if (font == null) {
712                                 throw new RTFException(rtf, "No font created");
713                         }
714
715                         if (font.Num == -1) {
716                                 throw new RTFException(rtf, "Missing font number");
717                         }
718
719                         rtf.RouteToken();
720                 }
721
722                 private void ReadColorTbl(RTF rtf) {
723                         Color   color;
724                         int     num;
725
726                         num = 0;
727
728                         while (true) {
729                                 rtf.GetToken();
730
731                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
732                                         break;
733                                 }
734
735                                 color = new Color(rtf);
736                                 color.Num = num++;
737
738                                 while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) {
739                                         switch (rtf.minor) {
740                                                 case Minor.Red: {
741                                                         color.Red = rtf.param;
742                                                         break;
743                                                 }
744
745                                                 case Minor.Green: {
746                                                         color.Green = rtf.param;
747                                                         break;
748                                                 }
749
750                                                 case Minor.Blue: {
751                                                         color.Blue = rtf.param;
752                                                         break;
753                                                 }
754                                         }
755
756                                         rtf.GetToken();
757                                 }
758                                 if (!rtf.CheckCM(TokenClass.Text, (Major)';')) {
759                                         throw new RTFException(rtf, "Malformed color entry");
760                                 }
761                         }
762                         rtf.RouteToken();
763                 }
764
765                 private void ReadStyleSheet(RTF rtf) {
766                         Style           style;
767                         StringBuilder   sb;
768
769                         sb = new StringBuilder();
770
771                         while (true) {
772                                 rtf.GetToken();
773
774                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
775                                         break;
776                                 }
777
778                                 style = new Style(rtf);
779
780                                 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
781                                         throw new RTFException(rtf, "Missing \"{\"");
782                                 }
783
784                                 while (true) {
785                                         rtf.GetToken();
786
787                                         if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) {
788                                                 break;
789                                         }
790
791                                         if (rtf.rtf_class == TokenClass.Control) {
792                                                 if (rtf.CheckMM(Major.SpecialChar, Minor.OptDest)) {
793                                                         continue;
794                                                 }
795                                                 if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) {
796                                                         style.Num = rtf.param;
797                                                         style.Type = StyleType.Paragraph;
798                                                         continue;
799                                                 }
800                                                 if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) {
801                                                         style.Num = rtf.param;
802                                                         style.Type = StyleType.Character;
803                                                         continue;
804                                                 }
805                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) {
806                                                         style.Num = rtf.param;
807                                                         style.Type = StyleType.Section;
808                                                         continue;
809                                                 }
810                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) {
811                                                         style.BasedOn = rtf.param;
812                                                         continue;
813                                                 }
814                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) {
815                                                         style.Additive = true;
816                                                         continue;
817                                                 }
818                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) {
819                                                         style.NextPar = rtf.param;
820                                                         continue;
821                                                 }
822
823                                                 new StyleElement(style, rtf.rtf_class, rtf.major, rtf.minor, rtf.param, rtf.text_buffer.ToString());
824                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
825                                                 // This passes over "{\*\keycode ... }, among other things
826                                                 rtf.SkipGroup();
827                                         } else if (rtf.rtf_class == TokenClass.Text) {
828                                                 while (rtf.rtf_class == TokenClass.Text) {
829                                                         if (rtf.major == (Major)';') {
830                                                                 rtf.UngetToken();
831                                                                 break;
832                                                         }
833
834                                                         sb.Append((char)rtf.major);
835                                                         rtf.GetToken();
836                                                 }
837
838                                                 style.Name = sb.ToString();
839 #if RTF_DEBUG
840                                         } else {
841                                                 Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer);
842 #endif
843                                         }
844                                 }
845                                 rtf.GetToken();
846
847                                 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
848                                         throw new RTFException(rtf, "Missing EndGroup (\"}\"");
849                                 }
850
851                                 // Sanity checks
852                                 if (style.Name == null) {
853                                         throw new RTFException(rtf, "Style must have name");
854                                 }
855
856                                 if (style.Num < 0) {
857                                         if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) {
858                                                 throw new RTFException(rtf, "Missing style number");
859                                         }
860
861                                         style.Num = Style.NormalStyleNum;
862                                 }
863
864                                 if (style.NextPar == -1) {
865                                         style.NextPar = style.Num;
866                                 }
867                         }
868
869                         rtf.RouteToken();
870                 }
871
872                 private void ReadInfoGroup(RTF rtf) {
873                         rtf.SkipGroup();
874                         rtf.RouteToken();
875                 }
876
877                 private void ReadPictGroup(RTF rtf) {
878                         rtf.SkipGroup();
879                         rtf.RouteToken();
880                 }
881
882                 private void ReadObjGroup(RTF rtf) {
883                         rtf.SkipGroup();
884                         rtf.RouteToken();
885                 }
886                 #endregion      // Default Delegates
887         }
888 }