2005-12-22 Peter Dennis Bartok <pbartok@novell.com>
[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                         if ((c = (char)source.Read()) != EOF) {
251                                 this.text_buffer.Append(c);
252                         }
253
254                         if (this.prev_char == EOF) {
255                                 this.bump_line = true;
256                         }
257
258                         old_bump_line = bump_line;
259                         bump_line = false;
260
261                         if (c == '\r') {
262                                 bump_line = true;
263                         } else if (c == '\n') {
264                                 bump_line = true;
265                                 if (this.prev_char == '\r') {
266                                         old_bump_line = false;
267                                 }
268                         }
269
270                         this.line_pos ++;
271                         if (old_bump_line) {
272                                 this.line_num++;
273                                 this.line_pos = 1;
274                         }
275
276                         this.prev_char = c;
277                         return c;
278                 }
279
280                 /// <summary>Parse the RTF stream</summary>
281                 public void Read() {
282                         while (GetToken() != TokenClass.EOF) {
283                                 RouteToken();
284                         }
285                 }
286
287                 /// <summary>Route a token</summary>
288                 public void RouteToken() {
289                         if (CheckCM(TokenClass.Control, Major.Destination)) {
290                                 DestinationDelegate d;
291
292                                 d = destination_callbacks[minor];
293                                 if (d != null) {
294                                         d(this);
295                                 }
296                         }
297
298                         // Invoke class callback if there is one
299                         ClassDelegate c;
300
301                         c = class_callbacks[rtf_class];
302                         if (c != null) {
303                                 c(this);
304                         }
305                         
306                 }
307
308                 /// <summary>Skip to the end of the current group</summary>
309                 public void SkipGroup() {
310                         int     level;
311
312                         level = 1;
313
314                         while (GetToken() != TokenClass.EOF) {
315                                 if (rtf_class == TokenClass.Group) {
316                                         if (this.major == Major.BeginGroup) {
317                                                 level++;
318                                         } else if (this.major == Major.EndGroup) {
319                                                 level--;
320                                                 if (level < 1) {
321                                                         break;
322                                                 }
323                                         }
324                                 }
325                         }
326                 }
327
328                 /// <summary>Return the next token in the stream</summary>
329                 public TokenClass GetToken() {
330                         if (pushed_class != TokenClass.None) {
331                                 this.rtf_class = this.pushed_class;
332                                 this.major = this.pushed_major;
333                                 this.minor = this.pushed_minor;
334                                 this.param = this.pushed_param;
335                                 this.pushed_class = TokenClass.None;
336                                 return this.rtf_class;
337                         }
338
339                         GetToken2();
340
341                         if (this.rtf_class == TokenClass.Text) {
342                                 this.minor = (Minor)this.cur_charset[(int)this.major];
343                         }
344
345                         if (this.cur_charset.Flags == CharsetFlags.None) {
346                                 return this.rtf_class;
347                         }
348
349                         if (((this.cur_charset.Flags & CharsetFlags.Read) != 0) && CheckCM(TokenClass.Control, Major.CharSet)) {
350                                 this.cur_charset.ReadMap();
351                         } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
352                                 Font    fp;
353
354                                 fp = Font.GetFont(this.font_list, this.param);
355
356                                 if (fp != null) {
357                                         if (fp.Name.StartsWith("Symbol")) {
358                                                 this.cur_charset.ID = CharsetType.Symbol;
359                                         } else {
360                                                 this.cur_charset.ID = CharsetType.General;
361                                         }
362                                 } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) {
363                                         switch(this.major) {
364                                                 case Major.BeginGroup: {
365                                                         this.charset_stack.Push(this.cur_charset);
366                                                         break;
367                                                 }
368
369                                                 case Major.EndGroup: {
370                                                         this.cur_charset = (Charset)this.charset_stack.Pop();
371                                                         break;
372                                                 }
373                                         }
374                                 }
375                         }
376
377                         return this.rtf_class;
378                 }
379
380                 private void GetToken2() {
381                         char    c;
382                         int     sign;
383
384                         this.rtf_class = TokenClass.Unknown;
385                         this.param = NoParam;
386
387                         this.text_buffer.Length = 0;
388
389                         if (this.pushed_char != EOF) {
390                                 c = this.pushed_char;
391                                 this.text_buffer.Append(c);
392                                 this.pushed_char = EOF;
393                         } else if ((c = GetChar()) == EOF) {
394                                 this.rtf_class = TokenClass.EOF;
395                                 return;
396                         }
397
398                         if (c == '{') {
399                                 this.rtf_class = TokenClass.Group;
400                                 this.major = Major.BeginGroup;
401                                 return;
402                         }
403
404                         if (c == '}') {
405                                 this.rtf_class = TokenClass.Group;
406                                 this.major = Major.EndGroup;
407                                 return;
408                         }
409
410                         if (c != '\\') {
411                                 if (c != '\t') {
412                                         this.rtf_class = TokenClass.Text;
413                                         this.major = (Major)c;  // FIXME - typing?
414                                         return;
415                                 } else {
416                                         this.rtf_class = TokenClass.Control;
417                                         this.major = Major.SpecialChar;
418                                         this.minor = Minor.Tab;
419                                         return;
420                                 }
421                         }
422
423                         if ((c = GetChar()) == EOF) {
424                                 // Not so good
425                                 return;
426                         }
427
428                         if (!Char.IsLetter(c)) {
429                                 if (c == '\'') {
430                                         char c2;
431
432                                         if ((c = GetChar()) == EOF) {
433                                                 return;
434                                         }
435
436                                         if ((c2 = GetChar()) == EOF) {
437                                                 return;
438                                         }
439
440                                         this.rtf_class = TokenClass.Text;
441                                         this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16))));
442                                         return;
443                                 }
444
445                                 // Escaped char
446                                 if (c == ':' || c == '{' || c == '}' || c == '\\') {
447                                         this.rtf_class = TokenClass.Text;
448                                         this.major = (Major)c;
449                                         return;
450                                 }
451
452                                 Lookup(this.text_buffer.ToString());
453                                 return;
454                         }
455
456                         while (Char.IsLetter(c)) {
457                                 if ((c = GetChar()) == EOF) {
458                                         break;
459                                 }
460                         }
461
462                         if (c != EOF) {
463                                 this.text_buffer.Length--;
464                         }
465
466                         Lookup(this.text_buffer.ToString());
467
468                         if (c != EOF) {
469                                 this.text_buffer.Append(c);
470                         }
471
472                         sign = 1;
473                         if (c == '-') {
474                                 sign = -1;
475                                 c = GetChar();
476                         }
477
478                         if (c != EOF && Char.IsDigit(c)) {
479                                 this.param = 0;
480                                 while (Char.IsDigit(c)) {
481                                         this.param = this.param * 10 + Convert.ToByte(c) - 48;
482                                         if ((c = GetChar()) == EOF) {
483                                                 break;
484                                         }
485                                 }
486                                 this.param *= sign;
487                         }
488
489                         if (c != EOF) {
490                                 if (c != ' ') {
491                                         this.pushed_char = c;
492                                 }
493                                 this.text_buffer.Length--;
494                         }
495                 }
496
497                 public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) {
498                         this.rtf_class = cl;
499                         this.major = maj;
500                         this.minor = min;
501                         this.param = par;
502                         if (par == NoParam) {
503                                 this.text_buffer = new StringBuilder(text);
504                         } else {
505                                 this.text_buffer = new StringBuilder(text + par.ToString());
506                         }
507                 }
508
509                 public void UngetToken() {
510                         if (this.pushed_class != TokenClass.None) {
511                                 throw new RTFException(this, "Cannot unget more than one token");
512                         }
513
514                         if (this.rtf_class == TokenClass.None) {
515                                 throw new RTFException(this, "No token to unget");
516                         }
517
518                         this.pushed_class = this.rtf_class;
519                         this.pushed_major = this.major;
520                         this.pushed_minor = this.minor;
521                         this.pushed_param = this.param;
522                         this.pushed_text_buffer = new StringBuilder(this.text_buffer.ToString());
523                 }
524
525                 public TokenClass PeekToken() {
526                         GetToken();
527                         UngetToken();
528                         return rtf_class;
529                 }
530
531                 public void Lookup(string token) {
532                         Object          obj;
533                         KeyStruct       key;
534
535                         obj = key_table[token.Substring(1)];
536                         if (obj == null) {
537                                 rtf_class = TokenClass.Unknown;
538                                 return;
539                         }
540
541                         key = (KeyStruct)obj;
542                         this.rtf_class = TokenClass.Control;
543                         this.major = key.Major;
544                         this.minor = key.Minor;
545                 }
546
547                 public bool CheckCM(TokenClass rtf_class, Major major) {
548                         if ((this.rtf_class == rtf_class) && (this.major == major)) {
549                                 return true;
550                         }
551
552                         return false;
553                 }
554
555                 public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) {
556                         if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) {
557                                 return true;
558                         }
559
560                         return false;
561                 }
562
563                 public bool CheckMM(Major major, Minor minor) {
564                         if ((this.major == major) && (this.minor == minor)) {
565                                 return true;
566                         }
567
568                         return false;
569                 }
570                 #endregion      // Methods
571
572                 #region Default Delegates
573                 private void ReadFontTbl(RTF rtf) {
574                         int     old;
575                         Font    font;
576
577                         old = -1;
578                         font = null;
579
580                         while (true) {
581                                 rtf.GetToken();
582
583                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
584                                         break;
585                                 }
586
587                                 if (old < 0) {
588                                         if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
589                                                 old = 1;
590                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
591                                                 old = 0;
592                                         } else {
593                                                 throw new RTFException(rtf, "Cannot determine format");
594                                         }
595                                 }
596
597                                 if (old == 0) {
598                                         if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
599                                                 throw new RTFException(rtf, "missing \"{\"");
600                                         }
601                                         rtf.GetToken();
602                                 }
603
604                                 font = new Font(rtf);
605
606                                 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
607                                         if (rtf.rtf_class == TokenClass.Control) {
608                                                 switch(rtf.major) {
609                                                         case Major.FontFamily: {
610                                                                 font.Family = (int)rtf.minor;
611                                                                 break;
612                                                         }
613
614                                                         case Major.CharAttr: {
615                                                                 switch(rtf.minor) {
616                                                                         case Minor.FontNum: {
617                                                                                 font.Num = rtf.param;
618                                                                                 break;
619                                                                         }
620
621                                                                         default: {
622                                                                                 #if RTF_DEBUG
623                                                                                         Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor);
624                                                                                 #endif
625                                                                                 break;
626                                                                         }
627                                                                 }
628                                                                 break;
629                                                         }
630
631                                                         case Major.FontAttr: {
632                                                                 switch (rtf.minor) {
633                                                                         case Minor.FontCharSet: {
634                                                                                 font.Charset = (CharsetType)rtf.param;
635                                                                                 break;
636                                                                         }
637
638                                                                         case Minor.FontPitch: {
639                                                                                 font.Pitch = rtf.param;
640                                                                                 break;
641                                                                         }
642
643                                                                         case Minor.FontCodePage: {
644                                                                                 font.Codepage = rtf.param;
645                                                                                 break;
646                                                                         }
647
648                                                                         case Minor.FTypeNil:
649                                                                         case Minor.FTypeTrueType: {
650                                                                                 font.Type = rtf.param;
651                                                                                 break;
652                                                                         }
653                                                                         default: {
654                                                                                 #if RTF_DEBUG
655                                                                                         Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor);
656                                                                                 #endif
657                                                                                 break;
658                                                                         }
659                                                                 }
660                                                                 break;
661                                                         }
662
663                                                         default: {
664                                                                 #if RTF_DEBUG
665                                                                         Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major);
666                                                                 #endif
667                                                                 break;
668                                                         }
669                                                 }
670                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
671                                                 rtf.SkipGroup();
672                                         } else if (rtf.rtf_class == TokenClass.Text) {
673                                                 StringBuilder   sb;
674
675                                                 sb = new StringBuilder();
676
677                                                 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
678                                                         sb.Append((char)rtf.major);
679                                                         rtf.GetToken();
680                                                 }
681
682                                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
683                                                         rtf.UngetToken();
684                                                 }
685
686                                                 font.Name = sb.ToString();
687                                                 continue;
688 #if RTF_DEBUG
689                                         } else {
690                                                 Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer);
691 #endif
692                                         }
693
694                                         rtf.GetToken();
695                                 }
696
697                                 if (old == 0) {
698                                         rtf.GetToken();
699
700                                         if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
701                                                 throw new RTFException(rtf, "Missing \"}\"");
702                                         }
703                                 }
704                         }
705
706                         if (font == null) {
707                                 throw new RTFException(rtf, "No font created");
708                         }
709
710                         if (font.Num == -1) {
711                                 throw new RTFException(rtf, "Missing font number");
712                         }
713
714                         rtf.RouteToken();
715                 }
716
717                 private void ReadColorTbl(RTF rtf) {
718                         Color   color;
719                         int     num;
720
721                         num = 0;
722
723                         while (true) {
724                                 rtf.GetToken();
725
726                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
727                                         break;
728                                 }
729
730                                 color = new Color(rtf);
731                                 color.Num = num++;
732
733                                 while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) {
734                                         switch (rtf.minor) {
735                                                 case Minor.Red: {
736                                                         color.Red = rtf.param;
737                                                         break;
738                                                 }
739
740                                                 case Minor.Green: {
741                                                         color.Green = rtf.param;
742                                                         break;
743                                                 }
744
745                                                 case Minor.Blue: {
746                                                         color.Blue = rtf.param;
747                                                         break;
748                                                 }
749                                         }
750
751                                         rtf.GetToken();
752                                 }
753                                 if (!rtf.CheckCM(TokenClass.Text, (Major)';')) {
754                                         throw new RTFException(rtf, "Malformed color entry");
755                                 }
756                         }
757                         rtf.RouteToken();
758                 }
759
760                 private void ReadStyleSheet(RTF rtf) {
761                         Style           style;
762                         StringBuilder   sb;
763
764                         sb = new StringBuilder();
765
766                         while (true) {
767                                 rtf.GetToken();
768
769                                 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
770                                         break;
771                                 }
772
773                                 style = new Style(rtf);
774
775                                 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
776                                         throw new RTFException(rtf, "Missing \"{\"");
777                                 }
778
779                                 while (true) {
780                                         rtf.GetToken();
781
782                                         if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) {
783                                                 break;
784                                         }
785
786                                         if (rtf.rtf_class == TokenClass.Control) {
787                                                 if (rtf.CheckMM(Major.SpecialChar, Minor.OptDest)) {
788                                                         continue;
789                                                 }
790                                                 if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) {
791                                                         style.Num = rtf.param;
792                                                         style.Type = StyleType.Paragraph;
793                                                         continue;
794                                                 }
795                                                 if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) {
796                                                         style.Num = rtf.param;
797                                                         style.Type = StyleType.Character;
798                                                         continue;
799                                                 }
800                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) {
801                                                         style.Num = rtf.param;
802                                                         style.Type = StyleType.Section;
803                                                         continue;
804                                                 }
805                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) {
806                                                         style.BasedOn = rtf.param;
807                                                         continue;
808                                                 }
809                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) {
810                                                         style.Additive = true;
811                                                         continue;
812                                                 }
813                                                 if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) {
814                                                         style.NextPar = rtf.param;
815                                                         continue;
816                                                 }
817
818                                                 new StyleElement(style, rtf.rtf_class, rtf.major, rtf.minor, rtf.param, rtf.text_buffer.ToString());
819                                         } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
820                                                 // This passes over "{\*\keycode ... }, among other things
821                                                 rtf.SkipGroup();
822                                         } else if (rtf.rtf_class == TokenClass.Text) {
823                                                 while (rtf.rtf_class == TokenClass.Text) {
824                                                         if (rtf.major == (Major)';') {
825                                                                 rtf.UngetToken();
826                                                                 break;
827                                                         }
828
829                                                         sb.Append((char)rtf.major);
830                                                         rtf.GetToken();
831                                                 }
832
833                                                 style.Name = sb.ToString();
834 #if RTF_DEBUG
835                                         } else {
836                                                 Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer);
837 #endif
838                                         }
839                                 }
840                                 rtf.GetToken();
841
842                                 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
843                                         throw new RTFException(rtf, "Missing EndGroup (\"}\"");
844                                 }
845
846                                 // Sanity checks
847                                 if (style.Name == null) {
848                                         throw new RTFException(rtf, "Style must have name");
849                                 }
850
851                                 if (style.Num < 0) {
852                                         if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) {
853                                                 throw new RTFException(rtf, "Missing style number");
854                                         }
855
856                                         style.Num = Style.NormalStyleNum;
857                                 }
858
859                                 if (style.NextPar == -1) {
860                                         style.NextPar = style.Num;
861                                 }
862                         }
863
864                         rtf.RouteToken();
865                 }
866
867                 private void ReadInfoGroup(RTF rtf) {
868                         rtf.SkipGroup();
869                         rtf.RouteToken();
870                 }
871
872                 private void ReadPictGroup(RTF rtf) {
873                         rtf.SkipGroup();
874                         rtf.RouteToken();
875                 }
876
877                 private void ReadObjGroup(RTF rtf) {
878                         rtf.SkipGroup();
879                         rtf.RouteToken();
880                 }
881                 #endregion      // Default Delegates
882         }
883 }