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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok (pbartok@novell.com)
31 using System.Collections;
35 namespace System.Windows.Forms.RTF {
37 #region Local Variables
38 internal const char EOF = unchecked((char)-1);
39 internal const int NoParam = -1000000;
41 private TokenClass rtf_class;
46 private StringBuilder text_buffer;
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;
57 private char prev_char;
58 private bool bump_line;
60 private Font font_list;
61 private Color color_list;
62 private Style style_list;
64 private Charset cur_charset;
65 private Stack charset_stack;
71 private StreamReader source;
73 private static Hashtable key_table;
74 private static KeyStruct[] Keys = KeysInit.Init();
76 private DestinationCallback destination_callbacks;
77 private ClassCallback class_callbacks;
78 #endregion // Local Variables
82 key_table = new Hashtable(Keys.Length);
83 for (int i = 0; i < Keys.Length; i++) {
84 key_table[Keys[i].Symbol] = Keys[i];
88 public RTF(Stream stream) {
89 source = new StreamReader(stream);
91 text_buffer = new StringBuilder(1024);
92 pushed_text_buffer = new StringBuilder(1024);
94 rtf_class = TokenClass.None;
95 pushed_class = TokenClass.None;
96 pushed_char = unchecked((char)-1);
100 prev_char = unchecked((char)-1);
103 cur_charset = new Charset();
105 destination_callbacks = new DestinationCallback();
106 class_callbacks = new ClassCallback();
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);
115 #endregion // Constructors
118 public TokenClass TokenClass {
120 return this.rtf_class;
124 this.rtf_class = value;
160 return this.text_buffer.ToString();
165 this.text_buffer.Length = 0;
167 this.text_buffer = new StringBuilder(value);
172 public Color Colors {
182 public Style Styles {
202 public ClassCallback ClassCallback {
204 return class_callbacks;
208 class_callbacks = value;
212 public DestinationCallback DestinationCallback {
214 return destination_callbacks;
218 destination_callbacks = value;
222 public int LineNumber {
233 #endregion // Properties
236 /// <summary>Set the default font for documents without font table</summary>
237 public void DefaultFont(string name) {
240 font = new Font(this);
245 /// <summary>Read the next character from the input</summary>
246 private char GetChar() {
251 if ((c = (char)source.Read()) != EOF) {
252 this.text_buffer.Append(c);
255 if (this.prev_char == EOF) {
256 this.bump_line = true;
259 old_bump_line = bump_line;
264 text_buffer.Length--;
266 } else if (c == '\n') {
268 if (this.prev_char == '\r') {
269 old_bump_line = false;
271 text_buffer.Length--;
285 /// <summary>Parse the RTF stream</summary>
287 while (GetToken() != TokenClass.EOF) {
292 /// <summary>Route a token</summary>
293 public void RouteToken() {
294 if (CheckCM(TokenClass.Control, Major.Destination)) {
295 DestinationDelegate d;
297 d = destination_callbacks[minor];
303 // Invoke class callback if there is one
306 c = class_callbacks[rtf_class];
313 /// <summary>Skip to the end of the current group</summary>
314 public void SkipGroup() {
319 while (GetToken() != TokenClass.EOF) {
320 if (rtf_class == TokenClass.Group) {
321 if (this.major == Major.BeginGroup) {
323 } else if (this.major == Major.EndGroup) {
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;
346 if (this.rtf_class == TokenClass.Text) {
347 this.minor = (Minor)this.cur_charset[(int)this.major];
350 if (this.cur_charset.Flags == CharsetFlags.None) {
351 return this.rtf_class;
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)) {
359 fp = Font.GetFont(this.font_list, this.param);
362 if (fp.Name.StartsWith("Symbol")) {
363 this.cur_charset.ID = CharsetType.Symbol;
365 this.cur_charset.ID = CharsetType.General;
367 } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) {
369 case Major.BeginGroup: {
370 this.charset_stack.Push(this.cur_charset);
374 case Major.EndGroup: {
375 this.cur_charset = (Charset)this.charset_stack.Pop();
382 return this.rtf_class;
385 private void GetToken2() {
389 this.rtf_class = TokenClass.Unknown;
390 this.param = NoParam;
392 this.text_buffer.Length = 0;
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;
404 this.rtf_class = TokenClass.Group;
405 this.major = Major.BeginGroup;
410 this.rtf_class = TokenClass.Group;
411 this.major = Major.EndGroup;
417 this.rtf_class = TokenClass.Text;
418 this.major = (Major)c; // FIXME - typing?
421 this.rtf_class = TokenClass.Control;
422 this.major = Major.SpecialChar;
423 this.minor = Minor.Tab;
428 if ((c = GetChar()) == EOF) {
433 if (!Char.IsLetter(c)) {
437 if ((c = GetChar()) == EOF) {
441 if ((c2 = GetChar()) == EOF) {
445 this.rtf_class = TokenClass.Text;
446 this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16))));
451 if (c == ':' || c == '{' || c == '}' || c == '\\') {
452 this.rtf_class = TokenClass.Text;
453 this.major = (Major)c;
457 Lookup(this.text_buffer.ToString());
461 while (Char.IsLetter(c)) {
462 if ((c = GetChar()) == EOF) {
468 this.text_buffer.Length--;
471 Lookup(this.text_buffer.ToString());
474 this.text_buffer.Append(c);
483 if (c != EOF && Char.IsDigit(c)) {
485 while (Char.IsDigit(c)) {
486 this.param = this.param * 10 + Convert.ToByte(c) - 48;
487 if ((c = GetChar()) == EOF) {
496 this.pushed_char = c;
498 this.text_buffer.Length--;
502 public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) {
507 if (par == NoParam) {
508 this.text_buffer = new StringBuilder(text);
510 this.text_buffer = new StringBuilder(text + par.ToString());
514 public void UngetToken() {
515 if (this.pushed_class != TokenClass.None) {
516 throw new RTFException(this, "Cannot unget more than one token");
519 if (this.rtf_class == TokenClass.None) {
520 throw new RTFException(this, "No token to unget");
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());
530 public TokenClass PeekToken() {
536 public void Lookup(string token) {
540 obj = key_table[token.Substring(1)];
542 rtf_class = TokenClass.Unknown;
546 key = (KeyStruct)obj;
547 this.rtf_class = TokenClass.Control;
548 this.major = key.Major;
549 this.minor = key.Minor;
552 public bool CheckCM(TokenClass rtf_class, Major major) {
553 if ((this.rtf_class == rtf_class) && (this.major == major)) {
560 public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) {
561 if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) {
568 public bool CheckMM(Major major, Minor minor) {
569 if ((this.major == major) && (this.minor == minor)) {
575 #endregion // Methods
577 #region Default Delegates
578 private void ReadFontTbl(RTF rtf) {
588 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
593 if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
595 } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
598 throw new RTFException(rtf, "Cannot determine format");
603 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
604 throw new RTFException(rtf, "missing \"{\"");
609 font = new Font(rtf);
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) {
614 case Major.FontFamily: {
615 font.Family = (int)rtf.minor;
619 case Major.CharAttr: {
621 case Minor.FontNum: {
622 font.Num = rtf.param;
628 Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor);
636 case Major.FontAttr: {
638 case Minor.FontCharSet: {
639 font.Charset = (CharsetType)rtf.param;
643 case Minor.FontPitch: {
644 font.Pitch = rtf.param;
648 case Minor.FontCodePage: {
649 font.Codepage = rtf.param;
654 case Minor.FTypeTrueType: {
655 font.Type = rtf.param;
660 Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor);
670 Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major);
675 } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
677 } else if (rtf.rtf_class == TokenClass.Text) {
680 sb = new StringBuilder();
682 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
683 sb.Append((char)rtf.major);
687 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
691 font.Name = sb.ToString();
695 Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer);
705 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
706 throw new RTFException(rtf, "Missing \"}\"");
712 throw new RTFException(rtf, "No font created");
715 if (font.Num == -1) {
716 throw new RTFException(rtf, "Missing font number");
722 private void ReadColorTbl(RTF rtf) {
731 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
735 color = new Color(rtf);
738 while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) {
741 color.Red = rtf.param;
746 color.Green = rtf.param;
751 color.Blue = rtf.param;
758 if (!rtf.CheckCM(TokenClass.Text, (Major)';')) {
759 throw new RTFException(rtf, "Malformed color entry");
765 private void ReadStyleSheet(RTF rtf) {
769 sb = new StringBuilder();
774 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
778 style = new Style(rtf);
780 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
781 throw new RTFException(rtf, "Missing \"{\"");
787 if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) {
791 if (rtf.rtf_class == TokenClass.Control) {
792 if (rtf.CheckMM(Major.SpecialChar, Minor.OptDest)) {
795 if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) {
796 style.Num = rtf.param;
797 style.Type = StyleType.Paragraph;
800 if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) {
801 style.Num = rtf.param;
802 style.Type = StyleType.Character;
805 if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) {
806 style.Num = rtf.param;
807 style.Type = StyleType.Section;
810 if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) {
811 style.BasedOn = rtf.param;
814 if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) {
815 style.Additive = true;
818 if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) {
819 style.NextPar = rtf.param;
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
827 } else if (rtf.rtf_class == TokenClass.Text) {
828 while (rtf.rtf_class == TokenClass.Text) {
829 if (rtf.major == (Major)';') {
834 sb.Append((char)rtf.major);
838 style.Name = sb.ToString();
841 Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer);
847 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
848 throw new RTFException(rtf, "Missing EndGroup (\"}\"");
852 if (style.Name == null) {
853 throw new RTFException(rtf, "Style must have name");
857 if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) {
858 throw new RTFException(rtf, "Missing style number");
861 style.Num = Style.NormalStyleNum;
864 if (style.NextPar == -1) {
865 style.NextPar = style.Num;
872 private void ReadInfoGroup(RTF rtf) {
877 private void ReadPictGroup(RTF rtf) {
882 private void ReadObjGroup(RTF rtf) {
886 #endregion // Default Delegates