diff options
Diffstat (limited to 'mcs/class/Managed.Windows.Forms/System.Windows.Forms.RTF/RTF.cs')
-rw-r--r-- | mcs/class/Managed.Windows.Forms/System.Windows.Forms.RTF/RTF.cs | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms.RTF/RTF.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms.RTF/RTF.cs new file mode 100644 index 00000000000..689deafa8a4 --- /dev/null +++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms.RTF/RTF.cs @@ -0,0 +1,883 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok (pbartok@novell.com) +// + +// COMPLETE + +#undef RTF_DEBUG + +using System; +using System.Collections; +using System.IO; +using System.Text; + +namespace System.Windows.Forms.RTF { + internal class RTF { + #region Local Variables + internal const char EOF = unchecked((char)-1); + internal const int NoParam = -1000000; + + private TokenClass rtf_class; + private Major major; + private Minor minor; + private int param; + private int format; + private StringBuilder text_buffer; + private int line_num; + private int line_pos; + + private char pushed_char; + private StringBuilder pushed_text_buffer; + private TokenClass pushed_class; + private Major pushed_major; + private Minor pushed_minor; + private int pushed_param; + + private char prev_char; + private bool bump_line; + + private Font font_list; + private Color color_list; + private Style style_list; + + private Charset cur_charset; + private Stack charset_stack; + + private Style styles; + private Color colors; + private Font fonts; + + private StreamReader source; + + private static Hashtable key_table; + private static KeyStruct[] Keys = KeysInit.Init(); + + private DestinationCallback destination_callbacks; + private ClassCallback class_callbacks; + #endregion // Local Variables + + #region Constructors + static RTF() { + key_table = new Hashtable(Keys.Length); + for (int i = 0; i < Keys.Length; i++) { + key_table[Keys[i].Symbol] = Keys[i]; + } + } + + public RTF(Stream stream) { + source = new StreamReader(stream); + + text_buffer = new StringBuilder(1024); + pushed_text_buffer = new StringBuilder(1024); + + rtf_class = TokenClass.None; + pushed_class = TokenClass.None; + pushed_char = unchecked((char)-1); + + line_num = 0; + line_pos = 0; + prev_char = unchecked((char)-1); + bump_line = false; + + cur_charset = new Charset(); + + destination_callbacks = new DestinationCallback(); + class_callbacks = new ClassCallback(); + + destination_callbacks[Minor.FontTbl] = new DestinationDelegate(ReadFontTbl); + destination_callbacks[Minor.ColorTbl] = new DestinationDelegate(ReadColorTbl); + destination_callbacks[Minor.StyleSheet] = new DestinationDelegate(ReadStyleSheet); + destination_callbacks[Minor.Info] = new DestinationDelegate(ReadInfoGroup); + destination_callbacks[Minor.Pict] = new DestinationDelegate(ReadPictGroup); + destination_callbacks[Minor.Object] = new DestinationDelegate(ReadObjGroup); + } + #endregion // Constructors + + #region Properties + public TokenClass TokenClass { + get { + return this.rtf_class; + } + + set { + this.rtf_class = value; + } + } + + public Major Major { + get { + return this.major; + } + + set { + this.major = value; + } + } + + public Minor Minor { + get { + return this.minor; + } + + set { + this.minor = value; + } + } + + public int Param { + get { + return this.param; + } + + set { + this.param = value; + } + } + + public string Text { + get { + return this.text_buffer.ToString(); + } + + set { + if (value == null) { + this.text_buffer.Length = 0; + } else { + this.text_buffer = new StringBuilder(value); + } + } + } + + public Color Colors { + get { + return colors; + } + + set { + colors = value; + } + } + + public Style Styles { + get { + return styles; + } + + set { + styles = value; + } + } + + public Font Fonts { + get { + return fonts; + } + + set { + fonts = value; + } + } + + public ClassCallback ClassCallback { + get { + return class_callbacks; + } + + set { + class_callbacks = value; + } + } + + public DestinationCallback DestinationCallback { + get { + return destination_callbacks; + } + + set { + destination_callbacks = value; + } + } + + public int LineNumber { + get { + return line_num; + } + } + + public int LinePos { + get { + return line_pos; + } + } + #endregion // Properties + + #region Methods + /// <summary>Set the default font for documents without font table</summary> + public void DefaultFont(string name) { + Font font; + + font = new Font(this); + font.Num = 0; + font.Name = name; + } + + /// <summary>Read the next character from the input</summary> + private char GetChar() { + char c; + bool old_bump_line; + + if ((c = (char)source.Read()) != EOF) { + this.text_buffer.Append(c); + } + + if (this.prev_char == EOF) { + this.bump_line = true; + } + + old_bump_line = bump_line; + bump_line = false; + + if (c == '\r') { + bump_line = true; + } else if (c == '\n') { + bump_line = true; + if (this.prev_char == '\r') { + old_bump_line = false; + } + } + + this.line_pos ++; + if (old_bump_line) { + this.line_num++; + this.line_pos = 1; + } + + this.prev_char = c; + return c; + } + + /// <summary>Parse the RTF stream</summary> + public void Read() { + while (GetToken() != TokenClass.EOF) { + RouteToken(); + } + } + + /// <summary>Route a token</summary> + public void RouteToken() { + if (CheckCM(TokenClass.Control, Major.Destination)) { + DestinationDelegate d; + + d = destination_callbacks[minor]; + if (d != null) { + d(this); + } + } + + // Invoke class callback if there is one + ClassDelegate c; + + c = class_callbacks[rtf_class]; + if (c != null) { + c(this); + } + + } + + /// <summary>Skip to the end of the current group</summary> + public void SkipGroup() { + int level; + + level = 1; + + while (GetToken() != TokenClass.EOF) { + if (rtf_class == TokenClass.Group) { + if (this.major == Major.BeginGroup) { + level++; + } else if (this.major == Major.EndGroup) { + level--; + if (level < 1) { + break; + } + } + } + } + } + + /// <summary>Return the next token in the stream</summary> + public TokenClass GetToken() { + if (pushed_class != TokenClass.None) { + this.rtf_class = this.pushed_class; + this.major = this.pushed_major; + this.minor = this.pushed_minor; + this.param = this.pushed_param; + this.pushed_class = TokenClass.None; + return this.rtf_class; + } + + GetToken2(); + + if (this.rtf_class == TokenClass.Text) { + this.minor = (Minor)this.cur_charset[(int)this.major]; + } + + if (this.cur_charset.Flags == CharsetFlags.None) { + return this.rtf_class; + } + + if (((this.cur_charset.Flags & CharsetFlags.Read) != 0) && CheckCM(TokenClass.Control, Major.CharSet)) { + this.cur_charset.ReadMap(); + } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) { + Font fp; + + fp = Font.GetFont(this.font_list, this.param); + + if (fp != null) { + if (fp.Name.StartsWith("Symbol")) { + this.cur_charset.ID = CharsetType.Symbol; + } else { + this.cur_charset.ID = CharsetType.General; + } + } else if (((this.cur_charset.Flags & CharsetFlags.Switch) != 0) && (this.rtf_class == TokenClass.Group)) { + switch(this.major) { + case Major.BeginGroup: { + this.charset_stack.Push(this.cur_charset); + break; + } + + case Major.EndGroup: { + this.cur_charset = (Charset)this.charset_stack.Pop(); + break; + } + } + } + } + + return this.rtf_class; + } + + private void GetToken2() { + char c; + int sign; + + this.rtf_class = TokenClass.Unknown; + this.param = NoParam; + + this.text_buffer.Length = 0; + + if (this.pushed_char != EOF) { + c = this.pushed_char; + this.text_buffer.Append(c); + this.pushed_char = EOF; + } else if ((c = GetChar()) == EOF) { + this.rtf_class = TokenClass.EOF; + return; + } + + if (c == '{') { + this.rtf_class = TokenClass.Group; + this.major = Major.BeginGroup; + return; + } + + if (c == '}') { + this.rtf_class = TokenClass.Group; + this.major = Major.EndGroup; + return; + } + + if (c != '\\') { + if (c != '\t') { + this.rtf_class = TokenClass.Text; + this.major = (Major)c; // FIXME - typing? + return; + } else { + this.rtf_class = TokenClass.Control; + this.major = Major.SpecialChar; + this.minor = Minor.Tab; + return; + } + } + + if ((c = GetChar()) == EOF) { + // Not so good + return; + } + + if (!Char.IsLetter(c)) { + if (c == '\'') { + char c2; + + if ((c = GetChar()) == EOF) { + return; + } + + if ((c2 = GetChar()) == EOF) { + return; + } + + this.rtf_class = TokenClass.Text; + this.major = (Major)((Char)((Convert.ToByte(c.ToString(), 16) * 16 + Convert.ToByte(c2.ToString(), 16)))); + return; + } + + // Escaped char + if (c == ':' || c == '{' || c == '}' || c == '\\') { + this.rtf_class = TokenClass.Text; + this.major = (Major)c; + return; + } + + Lookup(this.text_buffer.ToString()); + return; + } + + while (Char.IsLetter(c)) { + if ((c = GetChar()) == EOF) { + break; + } + } + + if (c != EOF) { + this.text_buffer.Length--; + } + + Lookup(this.text_buffer.ToString()); + + if (c != EOF) { + this.text_buffer.Append(c); + } + + sign = 1; + if (c == '-') { + sign = -1; + c = GetChar(); + } + + if (c != EOF && Char.IsDigit(c)) { + this.param = 0; + while (Char.IsDigit(c)) { + this.param = this.param * 10 + Convert.ToByte(c) - 48; + if ((c = GetChar()) == EOF) { + break; + } + } + this.param *= sign; + } + + if (c != EOF) { + if (c != ' ') { + this.pushed_char = c; + } + this.text_buffer.Length--; + } + } + + public void SetToken(TokenClass cl, Major maj, Minor min, int par, string text) { + this.rtf_class = cl; + this.major = maj; + this.minor = min; + this.param = par; + if (par == NoParam) { + this.text_buffer = new StringBuilder(text); + } else { + this.text_buffer = new StringBuilder(text + par.ToString()); + } + } + + public void UngetToken() { + if (this.pushed_class != TokenClass.None) { + throw new RTFException(this, "Cannot unget more than one token"); + } + + if (this.rtf_class == TokenClass.None) { + throw new RTFException(this, "No token to unget"); + } + + this.pushed_class = this.rtf_class; + this.pushed_major = this.major; + this.pushed_minor = this.minor; + this.pushed_param = this.param; + this.pushed_text_buffer = new StringBuilder(this.text_buffer.ToString()); + } + + public TokenClass PeekToken() { + GetToken(); + UngetToken(); + return rtf_class; + } + + public void Lookup(string token) { + Object obj; + KeyStruct key; + + obj = key_table[token.Substring(1)]; + if (obj == null) { + rtf_class = TokenClass.Unknown; + return; + } + + key = (KeyStruct)obj; + this.rtf_class = TokenClass.Control; + this.major = key.Major; + this.minor = key.Minor; + } + + public bool CheckCM(TokenClass rtf_class, Major major) { + if ((this.rtf_class == rtf_class) && (this.major == major)) { + return true; + } + + return false; + } + + public bool CheckCMM(TokenClass rtf_class, Major major, Minor minor) { + if ((this.rtf_class == rtf_class) && (this.major == major) && (this.minor == minor)) { + return true; + } + + return false; + } + + public bool CheckMM(Major major, Minor minor) { + if ((this.major == major) && (this.minor == minor)) { + return true; + } + + return false; + } + #endregion // Methods + + #region Default Delegates + private void ReadFontTbl(RTF rtf) { + int old; + Font font; + + old = -1; + font = null; + + while (true) { + rtf.GetToken(); + + if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + break; + } + + if (old < 0) { + if (rtf.CheckCMM(TokenClass.Control, Major.CharAttr, Minor.FontNum)) { + old = 1; + } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) { + old = 0; + } else { + throw new RTFException(rtf, "Cannot determine format"); + } + } + + if (old == 0) { + if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) { + throw new RTFException(rtf, "missing \"{\""); + } + rtf.GetToken(); + } + + font = new Font(rtf); + + while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) { + if (rtf.rtf_class == TokenClass.Control) { + switch(rtf.major) { + case Major.FontFamily: { + font.Family = (int)rtf.minor; + break; + } + + case Major.CharAttr: { + switch(rtf.minor) { + case Minor.FontNum: { + font.Num = rtf.param; + break; + } + + default: { + #if RTF_DEBUG + Console.WriteLine("Got unhandled Control.CharAttr.Minor: " + rtf.minor); + #endif + break; + } + } + break; + } + + case Major.FontAttr: { + switch (rtf.minor) { + case Minor.FontCharSet: { + font.Charset = (CharsetType)rtf.param; + break; + } + + case Minor.FontPitch: { + font.Pitch = rtf.param; + break; + } + + case Minor.FontCodePage: { + font.Codepage = rtf.param; + break; + } + + case Minor.FTypeNil: + case Minor.FTypeTrueType: { + font.Type = rtf.param; + break; + } + default: { + #if RTF_DEBUG + Console.WriteLine("Got unhandled Control.FontAttr.Minor: " + rtf.minor); + #endif + break; + } + } + break; + } + + default: { + #if RTF_DEBUG + Console.WriteLine("ReadFontTbl: Unknown Control token " + rtf.major); + #endif + break; + } + } + } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) { + rtf.SkipGroup(); + } else if (rtf.rtf_class == TokenClass.Text) { + StringBuilder sb; + + sb = new StringBuilder(); + + while ((rtf.rtf_class != TokenClass.EOF) && (!rtf.CheckCM(TokenClass.Text, (Major)';')) && (!rtf.CheckCM(TokenClass.Group, Major.EndGroup))) { + sb.Append((char)rtf.major); + rtf.GetToken(); + } + + if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + rtf.UngetToken(); + } + + font.Name = sb.ToString(); + continue; +#if RTF_DEBUG + } else { + Console.WriteLine("ReadFontTbl: Unknown token " + rtf.text_buffer); +#endif + } + + rtf.GetToken(); + } + + if (old == 0) { + rtf.GetToken(); + + if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + throw new RTFException(rtf, "Missing \"}\""); + } + } + } + + if (font == null) { + throw new RTFException(rtf, "No font created"); + } + + if (font.Num == -1) { + throw new RTFException(rtf, "Missing font number"); + } + + rtf.RouteToken(); + } + + private void ReadColorTbl(RTF rtf) { + Color color; + int num; + + num = 0; + + while (true) { + rtf.GetToken(); + + if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + break; + } + + color = new Color(rtf); + color.Num = num++; + + while (rtf.CheckCM(TokenClass.Control, Major.ColorName)) { + switch (rtf.minor) { + case Minor.Red: { + color.Red = rtf.param; + break; + } + + case Minor.Green: { + color.Green = rtf.param; + break; + } + + case Minor.Blue: { + color.Blue = rtf.param; + break; + } + } + + rtf.GetToken(); + } + if (!rtf.CheckCM(TokenClass.Text, (Major)';')) { + throw new RTFException(rtf, "Malformed color entry"); + } + } + rtf.RouteToken(); + } + + private void ReadStyleSheet(RTF rtf) { + Style style; + StringBuilder sb; + + sb = new StringBuilder(); + + while (true) { + rtf.GetToken(); + + if (rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + break; + } + + style = new Style(rtf); + + if (!rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) { + throw new RTFException(rtf, "Missing \"{\""); + } + + while (true) { + rtf.GetToken(); + + if ((rtf.rtf_class == TokenClass.EOF) || rtf.CheckCM(TokenClass.Text, (Major)';')) { + break; + } + + if (rtf.rtf_class == TokenClass.Control) { + if (rtf.CheckMM(Major.SpecialChar, Minor.OptDest)) { + continue; + } + if (rtf.CheckMM(Major.ParAttr, Minor.StyleNum)) { + style.Num = rtf.param; + style.Type = StyleType.Paragraph; + continue; + } + if (rtf.CheckMM(Major.CharAttr, Minor.CharStyleNum)) { + style.Num = rtf.param; + style.Type = StyleType.Character; + continue; + } + if (rtf.CheckMM(Major.StyleAttr, Minor.SectStyleNum)) { + style.Num = rtf.param; + style.Type = StyleType.Section; + continue; + } + if (rtf.CheckMM(Major.StyleAttr, Minor.BasedOn)) { + style.BasedOn = rtf.param; + continue; + } + if (rtf.CheckMM(Major.StyleAttr, Minor.Additive)) { + style.Additive = true; + continue; + } + if (rtf.CheckMM(Major.StyleAttr, Minor.Next)) { + style.NextPar = rtf.param; + continue; + } + + new StyleElement(style, rtf.rtf_class, rtf.major, rtf.minor, rtf.param, rtf.text_buffer.ToString()); + } else if (rtf.CheckCM(TokenClass.Group, Major.BeginGroup)) { + // This passes over "{\*\keycode ... }, among other things + rtf.SkipGroup(); + } else if (rtf.rtf_class == TokenClass.Text) { + while (rtf.rtf_class == TokenClass.Text) { + if (rtf.major == (Major)';') { + rtf.UngetToken(); + break; + } + + sb.Append((char)rtf.major); + rtf.GetToken(); + } + + style.Name = sb.ToString(); +#if RTF_DEBUG + } else { + Console.WriteLine("ReadStyleSheet: Ignored token " + rtf.text_buffer); +#endif + } + } + rtf.GetToken(); + + if (!rtf.CheckCM(TokenClass.Group, Major.EndGroup)) { + throw new RTFException(rtf, "Missing EndGroup (\"}\""); + } + + // Sanity checks + if (style.Name == null) { + throw new RTFException(rtf, "Style must have name"); + } + + if (style.Num < 0) { + if (!sb.ToString().StartsWith("Normal") && !sb.ToString().StartsWith("Standard")) { + throw new RTFException(rtf, "Missing style number"); + } + + style.Num = Style.NormalStyleNum; + } + + if (style.NextPar == -1) { + style.NextPar = style.Num; + } + } + + rtf.RouteToken(); + } + + private void ReadInfoGroup(RTF rtf) { + rtf.SkipGroup(); + rtf.RouteToken(); + } + + private void ReadPictGroup(RTF rtf) { + rtf.SkipGroup(); + rtf.RouteToken(); + } + + private void ReadObjGroup(RTF rtf) { + rtf.SkipGroup(); + rtf.RouteToken(); + } + #endregion // Default Delegates + } +} |