123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Drawing;
- using System.Text;
- namespace FastReport.RichTextParser
- {
- /// <summary>
- /// This class represents a RTF document header.
- /// </summary>
- /// <remarks>
- /// Refer to RTF-1.7 spec for detail description
- /// </remarks>
- class RTF_Header
- {
- private RichDocument doc;
- private enum HeaderParserState
- {
- Starting,
- Signature,
- GlobalProperties,
- SubitemFirstLevel,
- FontTable,
- ColorTable,
- StyleSheet,
- DocInfoSection,
- SkipFormatExtension
- }
- private enum StyleState { Wait, Parse };
- private bool header_active;
- private int recursion_counter;
- private HeaderParserState tag_state;
- private StyleState style_state;
- private Dictionary<long, uint> font_ids;
- public RichDocument Document { get { return doc; } }
- public RTF_Header(RichDocument doc)
- {
- this.doc = doc;
- this.doc.font_list = new List<RFont>();
- this.doc.color_list = new List<Color>();
- this.doc.color_list.Add(Color.Black);
- this.doc.style_list = new List<Style>();
- font_ids = new Dictionary<long, uint>();
- tag_state = HeaderParserState.Starting;
- style_state = StyleState.Wait;
- }
- internal bool Header
- {
- get { return header_active; }
- set
- {
- if (value == false)
- {
- if (doc.color_list.Count == 0)
- doc.color_list.Add(Color.Black);
- // Most simple RTF document does not include fonts, so we need add at least one
- if (doc.font_list.Count == 0)
- {
- RFont f = new RFont();
- f.family = RFont.Family.Rroman;
- f.FontName = "Arial";
- doc.font_list.Add(f);
- }
- }
- header_active = value;
- }
- }
- internal uint GetFontID(long id)
- {
- uint result;
- if (font_ids.ContainsKey(id))
- result = font_ids[id];
- else
- result = 0;
- return result;
- }
- public void StartParseEmbeddedDocument()
- {
- Header = true;
- tag_state = HeaderParserState.GlobalProperties;
- }
- /// <summary>
- /// Parser of RTF header.
- /// </summary>
- /// <remarks>
- /// Return false on finish of header
- /// </remarks>
- public bool Parse(RTF_Parser parser)
- {
- if (parser.Status == ParserStatus.OpenBlock)
- ++recursion_counter;
- if (parser.Status == ParserStatus.CloseBlock)
- {
- if (recursion_counter == 0)
- return false;
- --recursion_counter;
- }
- switch (tag_state)
- {
- case HeaderParserState.Starting:
- if (parser.Status != ParserStatus.OpenBlock || parser.Text.Length != 0 || parser.Control.Length != 0)
- throw new DecoderFallbackException("Not a RichText format");
- tag_state = HeaderParserState.Signature;
- Header = true;
- break;
- case HeaderParserState.Signature:
- if (parser.Control != "rtf")
- throw new Exception("Document format error");
- if (parser.Number > 1)
- throw new Exception("Unsupported RTF format version");
- tag_state = HeaderParserState.GlobalProperties;
- break;
- case HeaderParserState.GlobalProperties:
- switch (parser.Control)
- {
- case "ansi":
- // Do nothing?
- break;
- case "ansicpg":
- doc.codepage = parser.Number;
- break;
- case "deff":
- doc.default_font = parser.Number;
- break;
- case "deflang":
- doc.default_lang = parser.Number;
- break;
- case "deftab":
- doc.default_tab_width = parser.Number;
- break;
- case "pard":
- case "cf":
- case "fs":
- Header = false;
- break;
- case "uc":
- // Saw this TAG in header of file created with "\abHTML to RTF .Net"
- break;
- }
- if (parser.Status == ParserStatus.OpenBlock)
- tag_state = HeaderParserState.SubitemFirstLevel;
- else if (parser.Status == ParserStatus.CloseBlock)
- Header = false;
- break;
- case HeaderParserState.SubitemFirstLevel:
- {
- switch (parser.Control)
- {
- case "fonttbl":
- if (parser.Status == ParserStatus.CloseBlock)
- {
- // Empty font table
- break;
- }
- tag_state = HeaderParserState.FontTable;
- font_state = FontInfoState.FontID;
- doc.font_list.Clear(); // Delete default font
- break;
- case "colortbl":
- tag_state = HeaderParserState.ColorTable;
- if (parser.Delimiter == ' ')
- break;
- parser.override_default_color = parser.Delimiter != ';';
- break;
- case "noqfpromote":
- // Ignore control word for moving info and stylesheet sections into header
- // how many such commands will be here? Who knows?
- break;
- case "info":
- tag_state = HeaderParserState.DocInfoSection;
- break;
- case "stylesheet":
- if (parser.Status == ParserStatus.CloseBlock)
- tag_state = HeaderParserState.SubitemFirstLevel;
- else
- {
- tag_state = HeaderParserState.StyleSheet;
- style = new Style();
- parser.ResetParagraphFormat();
- parser.ResetRunFormat();
- style_state = StyleState.Parse;
- }
- break;
- case "mmathPr":
- tag_state = HeaderParserState.SkipFormatExtension;
- break;
- case "":
- if (parser.Delimiter == '*')
- {
- tag_state = HeaderParserState.SkipFormatExtension;
- }
- break;
- default:
- Header = false;
- break;
- }
- }
- break;
- case HeaderParserState.FontTable:
- ParseFontTable(parser);
- break;
- case HeaderParserState.ColorTable:
- ParseColorTable(parser);
- break;
- case HeaderParserState.DocInfoSection:
- if (parser.Status == ParserStatus.CloseBlock)
- {
- if (recursion_counter == 1)
- tag_state = HeaderParserState.SubitemFirstLevel; ;
- }
- break;
- case HeaderParserState.StyleSheet:
- ParserStyleTable(parser);
- break;
- case HeaderParserState.SkipFormatExtension:
- if (parser.Status == ParserStatus.CloseBlock)
- {
- if (recursion_counter == 1)
- tag_state = HeaderParserState.SubitemFirstLevel;
- }
- break;
- }
- return Header;
- }
- public Style FindStyle(RTF_Parser parser, int styledef)
- {
- foreach (Style style in doc.style_list)
- {
- if (style.styledef == styledef)
- return style;
- }
- System.Diagnostics.Trace.WriteLine("RTF style not found. Create default style");
- Style new_style = new Style();
- new_style.styledef = styledef;
- new_style.stylename = "Autogenerated " + styledef.ToString();
- new_style.run_style = parser.current_run_format;
- new_style.paragraph_style = parser.current_paragraph_format;
- doc.style_list.Add(new_style);
- return new_style;
- }
- Style style;
- private void ParserStyleTable(RTF_Parser parser)
- {
- bool parsed = false;
- switch (style_state)
- {
- case StyleState.Wait:
- if (parser.Status == ParserStatus.OpenBlock)
- {
- style = new Style();
- parser.ResetParagraphFormat();
- parser.ResetRunFormat();
- //need to set 0 in order to understand whether the style changes the font size
- parser.current_run_format.font_size = 0;
- style_state = StyleState.Parse;
- }
- if (parser.Status == ParserStatus.CloseBlock)
- {
- if (recursion_counter == 1)
- tag_state = HeaderParserState.SubitemFirstLevel;
- }
- break;
- case StyleState.Parse:
- // Parse style here
- switch (parser.Control)
- {
- case "s":
- style.styledef = (int)parser.Number;
- break;
- case "sbasedon":
- break;
- default:
- parsed = RTF_DocumentParser.ParseParagraphFormat(parser);
- if (parsed)
- break;
- parsed = RTF_DocumentParser.ParseRunFormat(parser, this);
- break;
- }
- if (parser.Status == ParserStatus.CloseBlock)
- {
- if (parser.Text.Length != 0)
- {
- style.stylename = parser.Text;
- }
- // Add new style here
- style.paragraph_style = parser.current_paragraph_format;
- style.run_style = parser.current_run_format;
- style_state = StyleState.Wait;
- doc.style_list.Add(style);
- if (recursion_counter == 1)
- tag_state = HeaderParserState.SubitemFirstLevel;
- break;
- }
- break;
- }
- }
- byte color_red;
- byte color_green;
- byte color_blue;
- void ParseColorTable(RTF_Parser parser)
- {
- if (parser.Status != ParserStatus.CloseBlock)
- {
- if (parser.Number > 255)
- throw new Exception("Color value out of range");
- byte cl = (byte)parser.Number;
- switch (parser.Control)
- {
- case "red":
- color_red = cl;
- break;
- case "green":
- color_green = cl;
- break;
- case "blue":
- color_blue = cl;
- break;
- }
- //if (parser.Delimiter == ';')
- if (parser.Delimiter != '\\')
- {
- Color c = Color.FromArgb(color_red, color_green, color_blue);
- if (parser.override_default_color)
- {
- doc.color_list[0] = c;
- }
- else
- {
- doc.color_list.Add(c);
- }
- parser.override_default_color = false;
- }
- }
- else
- {
- if (recursion_counter == 1)
- tag_state = HeaderParserState.SubitemFirstLevel; ;
- }
- }
- enum FontInfoState
- {
- FontID,
- CheckThemAll,
- }
- FontInfoState font_state;
- RFont font;
- internal bool ParseFontAttributes(RTF_Parser parser)
- {
- bool status = true;
- switch (parser.Control)
- {
- case "fnil":
- font.family = RFont.Family.Nil;
- break;
- case "froman":
- font.family = RFont.Family.Rroman;
- break;
- case "fswiss":
- font.family = RFont.Family.Swiss;
- break;
- case "fmodern":
- font.family = RFont.Family.Modern;
- break;
- case "fscript":
- font.family = RFont.Family.Script;
- break;
- case "fdecor":
- font.family = RFont.Family.Decor;
- break;
- case "ftech":
- font.family = RFont.Family.Tech;
- break;
- case "fbidi":
- font.family = RFont.Family.Bidi;
- break;
- case "fcharset":
- font.charset = parser.Number;
- break;
- default:
- // Here is many options are skipped off
- // it is better to collect them in debug mode
- status = false;
- break;
- }
- return status;
- }
- void ParseFontTable(RTF_Parser parser)
- {
- // Propagate exit
- if (recursion_counter == 1 && parser.Status == ParserStatus.CloseBlock)
- {
- tag_state = HeaderParserState.SubitemFirstLevel;
- return;
- }
- int back = recursion_counter;
- if (parser.Status == ParserStatus.CloseBlock)
- ++back;
- else if (parser.Status == ParserStatus.OpenBlock)
- --back;
- switch (font_state)
- {
- case FontInfoState.FontID:
- switch (parser.Control)
- {
- case "f":
- font = new RFont();
- font.font_id = (uint)doc.font_list.Count;
- if (!font_ids.ContainsKey(parser.Number))
- font_ids.Add(parser.Number, font.font_id);
- else
- Debug.WriteLine("Duplication of font_id: " + font.font_id.ToString());
- font_state = FontInfoState.CheckThemAll;
- break;
- case "flomajor":
- case "fhimajor":
- case "fdbmajor":
- case "fbimajor":
- case "flominor":
- case "fhiminor":
- case "fdbminor":
- case "fbiminor":
- // We just ignore these extensions of modern versions of MS Word
- break;
- default:
- if (parser.Status != ParserStatus.OpenBlock)
- throw new Exception("RTF unknown font tag");
- break;
- }
- break;
- case FontInfoState.CheckThemAll:
- if (back == 3)
- {
- if (parser.Status != ParserStatus.CloseBlock)
- {
- ParseFontAttributes(parser);
- }
- else
- {
- string str = parser.Text;
- int len = str.Length;
- if (len > 0)
- {
- if (str[len - 1] == ';')
- font.FontName = str.TrimEnd(';');
- doc.font_list.Add(font);
- font_state = FontInfoState.FontID;
- }
- else
- throw new Exception("RTF malformed font name");
- }
- }
- break;
- }
- return;
- }
- }
- }
|