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