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() {
250 if ((c = (char)source.Read()) != EOF) {
251 this.text_buffer.Append(c);
254 if (this.prev_char == EOF) {
255 this.bump_line = true;
258 old_bump_line = bump_line;
263 } else if (c == '\n') {
265 if (this.prev_char == '\r') {
266 old_bump_line = false;
280 /// <summary>Parse the RTF stream</summary>
282 while (GetToken() != TokenClass.EOF) {
287 /// <summary>Route a token</summary>
288 public void RouteToken() {
289 if (CheckCM(TokenClass.Control, Major.Destination)) {
290 DestinationDelegate d;
292 d = destination_callbacks[minor];
298 // Invoke class callback if there is one
301 c = class_callbacks[rtf_class];
308 /// <summary>Skip to the end of the current group</summary>
309 public void SkipGroup() {
314 while (GetToken() != TokenClass.EOF) {
315 if (rtf_class == TokenClass.Group) {
316 if (this.major == Major.BeginGroup) {
318 } else if (this.major == Major.EndGroup) {
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;
341 if (this.rtf_class == TokenClass.Text) {
342 this.minor = (Minor)this.cur_charset[(int)this.major];
345 if (this.cur_charset.Flags == CharsetFlags.None) {
346 return this.rtf_class;
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)) {
354 fp = Font.GetFont(this.font_list, this.param);
357 if (fp.Name.StartsWith("Symbol")) {
358 this.cur_charset.ID = CharsetType.Symbol;
360 this.cur_charset.ID = CharsetType.General;
362 } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) {
364 case Major.BeginGroup: {
365 this.charset_stack.Push(this.cur_charset);
369 case Major.EndGroup: {
370 this.cur_charset = (Charset)this.charset_stack.Pop();
377 return this.rtf_class;
380 private void GetToken2() {
384 this.rtf_class = TokenClass.Unknown;
385 this.param = NoParam;
387 this.text_buffer.Length = 0;
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;
399 this.rtf_class = TokenClass.Group;
400 this.major = Major.BeginGroup;
405 this.rtf_class = TokenClass.Group;
406 this.major = Major.EndGroup;
412 this.rtf_class = TokenClass.Text;
413 this.major = (Major)c; // FIXME - typing?
416 this.rtf_class = TokenClass.Control;
417 this.major = Major.SpecialChar;
418 this.minor = Minor.Tab;
423 if ((c = GetChar()) == EOF) {
428 if (!Char.IsLetter(c)) {
432 if ((c = GetChar()) == EOF) {
436 if ((c2 = GetChar()) == EOF) {
440 this.rtf_class = TokenClass.Text;
441 this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16))));
446 if (c == ':' || c == '{' || c == '}' || c == '\\') {
447 this.rtf_class = TokenClass.Text;
448 this.major = (Major)c;
452 Lookup(this.text_buffer.ToString());
456 while (Char.IsLetter(c)) {
457 if ((c = GetChar()) == EOF) {
463 this.text_buffer.Length--;
466 Lookup(this.text_buffer.ToString());
469 this.text_buffer.Append(c);
478 if (c != EOF && Char.IsDigit(c)) {
480 while (Char.IsDigit(c)) {
481 this.param = this.param * 10 + Convert.ToByte(c) - 48;
482 if ((c = GetChar()) == EOF) {
491 this.pushed_char = c;
493 this.text_buffer.Length--;
497 public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) {
502 if (par == NoParam) {
503 this.text_buffer = new StringBuilder(text);
505 this.text_buffer = new StringBuilder(text + par.ToString());
509 public void UngetToken() {
510 if (this.pushed_class != TokenClass.None) {
511 throw new RTFException(this, "Cannot unget more than one token");
514 if (this.rtf_class == TokenClass.None) {
515 throw new RTFException(this, "No token to unget");
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());
525 public TokenClass PeekToken() {
531 public void Lookup(string token) {
535 obj = key_table[token.Substring(1)];
537 rtf_class = TokenClass.Unknown;
541 key = (KeyStruct)obj;
542 this.rtf_class = TokenClass.Control;
543 this.major = key.Major;
544 this.minor = key.Minor;
547 public bool CheckCM(TokenClass rtf_class, Major major) {
548 if ((this.rtf_class == rtf_class) && (this.major == major)) {
555 public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) {
556 if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) {
563 public bool CheckMM(Major major, Minor minor) {
564 if ((this.major == major) && (this.minor == minor)) {
570 #endregion // Methods
572 #region Default Delegates
573 private void ReadFontTbl(RTF rtf) {
583 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
588 if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) {
590 } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
593 throw new RTFException(rtf, "Cannot determine format");
598 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
599 throw new RTFException(rtf, "missing \"{\"");
604 font = new Font(rtf);
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) {
609 case Major.FontFamily: {
610 font.Family = (int)rtf.minor;
614 case Major.CharAttr: {
616 case Minor.FontNum: {
617 font.Num = rtf.param;
623 Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor);
631 case Major.FontAttr: {
633 case Minor.FontCharSet: {
634 font.Charset = (CharsetType)rtf.param;
638 case Minor.FontPitch: {
639 font.Pitch = rtf.param;
643 case Minor.FontCodePage: {
644 font.Codepage = rtf.param;
649 case Minor.FTypeTrueType: {
650 font.Type = rtf.param;
655 Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor);
665 Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major);
670 } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
672 } else if (rtf.rtf_class == TokenClass.Text) {
675 sb = new StringBuilder();
677 while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) {
678 sb.Append((char)rtf.major);
682 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
686 font.Name = sb.ToString();
690 Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer);
700 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
701 throw new RTFException(rtf, "Missing \"}\"");
707 throw new RTFException(rtf, "No font created");
710 if (font.Num == -1) {
711 throw new RTFException(rtf, "Missing font number");
717 private void ReadColorTbl(RTF rtf) {
726 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
730 color = new Color(rtf);
733 while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) {
736 color.Red = rtf.param;
741 color.Green = rtf.param;
746 color.Blue = rtf.param;
753 if (!rtf.CheckCM(TokenClass.Text, (Major)';')) {
754 throw new RTFException(rtf, "Malformed color entry");
760 private void ReadStyleSheet(RTF rtf) {
764 sb = new StringBuilder();
769 if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
773 style = new Style(rtf);
775 if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) {
776 throw new RTFException(rtf, "Missing \"{\"");
782 if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) {
786 if (rtf.rtf_class == TokenClass.Control) {
787 if (rtf.CheckMM(Major.SpecialChar, Minor.OptDest)) {
790 if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) {
791 style.Num = rtf.param;
792 style.Type = StyleType.Paragraph;
795 if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) {
796 style.Num = rtf.param;
797 style.Type = StyleType.Character;
800 if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) {
801 style.Num = rtf.param;
802 style.Type = StyleType.Section;
805 if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) {
806 style.BasedOn = rtf.param;
809 if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) {
810 style.Additive = true;
813 if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) {
814 style.NextPar = rtf.param;
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
822 } else if (rtf.rtf_class == TokenClass.Text) {
823 while (rtf.rtf_class == TokenClass.Text) {
824 if (rtf.major == (Major)';') {
829 sb.Append((char)rtf.major);
833 style.Name = sb.ToString();
836 Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer);
842 if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) {
843 throw new RTFException(rtf, "Missing EndGroup (\"}\"");
847 if (style.Name == null) {
848 throw new RTFException(rtf, "Style must have name");
852 if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) {
853 throw new RTFException(rtf, "Missing style number");
856 style.Num = Style.NormalStyleNum;
859 if (style.NextPar == -1) {
860 style.NextPar = style.Num;
867 private void ReadInfoGroup(RTF rtf) {
872 private void ReadPictGroup(RTF rtf) {
877 private void ReadObjGroup(RTF rtf) {
881 #endregion // Default Delegates