private static void ProcessEndElement(XmlReader reader, RTFCurrentState cs, RichTextBox rtb)
        {
            switch (reader.Name)
            {
                case "b":
                    cs.cf.dwEffects &= ~CFE.BOLD;
                    cs.cf.wWeight = FW.NORMAL;
                    cs.charFormatChanged = true;
                    break;
                case "i":
                    cs.cf.dwEffects &= ~CFE.ITALIC;
                    cs.charFormatChanged = true;
                    break;
                case "u":
                    cs.cf.dwEffects &= ~CFE.UNDERLINE;
                    cs.charFormatChanged = true;
                    break;
                case "s":
                    cs.cf.dwEffects &= ~CFE.STRIKEOUT;
                    cs.charFormatChanged = true;
                    break;
                case "sup":
                    cs.cf.dwEffects &= ~CFE.SUPERSCRIPT;
                    cs.charFormatChanged = true;
                    break;
                case "sub":
                    cs.cf.dwEffects &= ~CFE.SUBSCRIPT;
                    cs.charFormatChanged = true;
                    break;
                case "a":
                    int length = rtb.TextLength - cs.hyperlinkStart;

                    if (cs.hyperlink != null)
                    {
                        rtb.Select(cs.hyperlinkStart, length);
                        if (cs.hyperlink != rtb.SelectedText)
                        {
                            string rtfText = rtb.SelectedRtf;
                            int idx = rtfText.LastIndexOf('}');
                            if (idx != -1)
                            {
                                string head = rtfText.Substring(0, idx);
                                string tail = rtfText.Substring(idx);
                                rtb.SelectedRtf = head + @"\v #" + cs.hyperlink + @"\v0" + tail;
                                length = rtb.TextLength - cs.hyperlinkStart;
                            }
                        }
                        // reposition to final
                        rtb.Select(rtb.TextLength + 1, 0);
                    }
                    cs.links.Add(new KeyValuePair<int, int>(cs.hyperlinkStart, length));

                    cs.hyperlinkStart = -1;
                    break;
                case "p":
                    cs.pf = cs.spf.Pop();
                    cs.paraFormatChanged = true;
                    break;
                case "li":
                    cs.pf = cs.spf.Pop();
                    cs.paraFormatChanged = true;
                    break;
                case "font":
                    cs.cf = cs.scf.Pop();
                    cs.charFormatChanged = true;
                    break;
            }
        }
        private static void ProcessNode(RichTextBox rtb, HandleRef handleRef, XmlReader reader, RTFCurrentState cs)
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    ProcessElement(reader, cs, rtb);
                    break;
                case XmlNodeType.EndElement:
                    ProcessEndElement(reader, cs, rtb);
                    break;
                case XmlNodeType.Text:
                    string strData = reader.Value;
                    bool bNewParagraph = (strData.IndexOf("\r\n", 0) >= 0) || (strData.IndexOf("\n", 0) >= 0);

                    if (strData.Length > 0)
                    {
                        // now, add text to control
                        int nStartCache = rtb.SelectionStart;
                        rtb.SelectedText = strData;
                        rtb.Select(nStartCache, strData.Length);

                        // apply format
                        if (cs.paraFormatChanged)
                            SetParaFormat(handleRef, cs.pf);
                        if (cs.charFormatChanged)
                            SetCharFormat(handleRef, cs.cf);
                        cs.charFormatChanged = false;
                        cs.paraFormatChanged = false;

                        // reposition to final
                        rtb.Select(rtb.TextLength + 1, 0);

                        // new paragraph requires to reset alignment
                        if (bNewParagraph)
                        {
                            cs.pf.dwMask = PFM.ALIGNMENT | PFM.NUMBERING;
                            cs.pf.wAlignment = PFA.LEFT;
                            cs.pf.wNumbering = 0;
                            cs.paraFormatChanged = true;
                        }
                    }
                    break;
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    rtb.SelectedText = reader.Value;
                    break;
                case XmlNodeType.XmlDeclaration:
                case XmlNodeType.ProcessingInstruction:
                    break;
                case XmlNodeType.Comment:
                    break;
                default:
                    break;
            }
        }
        private static void ProcessElement(XmlReader reader, RTFCurrentState cs, RichTextBox rtb)
        {
            switch (reader.Name.ToLower())
            {
                case "b":
                    cs.cf.dwMask |= CFM.WEIGHT | CFM.BOLD;
                    cs.cf.dwEffects |= CFE.BOLD;
                    cs.cf.wWeight = FW.BOLD;
                    cs.charFormatChanged = true;
                    break;
                case "i":
                    cs.cf.dwMask |= CFM.ITALIC;
                    cs.cf.dwEffects |= CFE.ITALIC;
                    cs.charFormatChanged = true;
                    break;
                case "u":
                    cs.cf.dwMask |= CFM.UNDERLINE | CFM.UNDERLINETYPE;
                    cs.cf.dwEffects |= CFE.UNDERLINE;
                    cs.cf.bUnderlineType = CFU.UNDERLINE;
                    cs.charFormatChanged = true;
                    break;
                case "s":
                    cs.cf.dwMask |= CFM.STRIKEOUT;
                    cs.cf.dwEffects |= CFE.STRIKEOUT;
                    cs.charFormatChanged = true;
                    break;
                case "sup":
                    cs.cf.dwMask |= CFM.SUPERSCRIPT;
                    cs.cf.dwEffects |= CFE.SUPERSCRIPT;
                    cs.charFormatChanged = true;
                    break;
                case "sub":
                    cs.cf.dwMask |= CFM.SUBSCRIPT;
                    cs.cf.dwEffects |= CFE.SUBSCRIPT;
                    cs.charFormatChanged = true;
                    break;
                case "a":
                    cs.hyperlinkStart = rtb.TextLength;
                    cs.hyperlink = null;
                    while (reader.MoveToNextAttribute())
                    {
                        if (reader.Name.ToLower() == "href")
                        {
                            cs.hyperlink = reader.Value;
                        }
                    }
                    reader.MoveToElement();
                    break;
                case "p":
                    cs.spf.Push(cs.pf);
                    while (reader.MoveToNextAttribute())
                    {
                        if (reader.Name.ToLower() == "align")
                        {
                            if (reader.Value == "left")
                            {
                                cs.pf.dwMask |= PFM.ALIGNMENT;
                                cs.pf.wAlignment = PFA.LEFT;
                                cs.paraFormatChanged = true;
                            }
                            else if (reader.Value == "right")
                            {
                                cs.pf.dwMask |= PFM.ALIGNMENT;
                                cs.pf.wAlignment = PFA.RIGHT;
                                cs.paraFormatChanged = true;
                            }
                            else if (reader.Value == "center")
                            {
                                cs.pf.dwMask |= PFM.ALIGNMENT;
                                cs.pf.wAlignment = PFA.CENTER;
                                cs.paraFormatChanged = true;
                            }
                        }
                    }
                    reader.MoveToElement();
                    break;
                case "li":
                    cs.spf.Push(cs.pf);
                    if (cs.pf.wNumbering != PFN.BULLET)
                    {
                        cs.pf.dwMask |= PFM.NUMBERING;
                        cs.pf.wNumbering = PFN.BULLET;
                        cs.paraFormatChanged = true;
                    }
                    break;
                case "font":
                    cs.scf.Push(cs.cf);
                    string strFont = cs.cf.szFaceName;
                    int crFont = cs.cf.crTextColor;
                    int yHeight = cs.cf.yHeight;

                    while (reader.MoveToNextAttribute())
                    {
                        switch (reader.Name.ToLower())
                        {
                            case "face":
                                cs.cf.dwMask |= CFM.FACE;
                                strFont = reader.Value;
                                break;
                            case "size":
                                cs.cf.dwMask |= CFM.SIZE;
                                yHeight = int.Parse(reader.Value);
                                yHeight *= (20 * 5);
                                break;
                            case "color":
                                cs.cf.dwMask |= CFM.COLOR;
                                string text = reader.Value;
                                if (text.StartsWith("#"))
                                {
                                    string strCr = text.Substring(1);
                                    int nCr = Convert.ToInt32(strCr, 16);
                                    Color color = Color.FromArgb(nCr);
                                    crFont = GetCOLORREF(color);
                                }
                                else if (!int.TryParse(text, out crFont))
                                {
                                    Color color = Color.FromName(text);
                                    crFont = GetCOLORREF(color);
                                }
                                break;
                        }
                    }
                    reader.MoveToElement();

                    cs.cf.szFaceName = strFont;
                    cs.cf.crTextColor = crFont;
                    cs.cf.yHeight = yHeight;

                    cs.cf.dwEffects &= ~CFE.AUTOCOLOR;
                    cs.charFormatChanged = true;
                    break;
            }
        }
        public static void SetXHTMLText(this RichTextBox rtb, string xhtmlText)
        {
            if (EnvUtils.IsMonoRuntime())
            {
                SetXHTMLTextAsPlainText(rtb, xhtmlText);
                return;
            }

            rtb.Clear();
            RTFCurrentState cs = new RTFCurrentState();

            var handleRef = new HandleRef(rtb, rtb.Handle);
            cs.cf = GetDefaultCharFormat(handleRef); // to apply character formatting
            cs.pf = GetDefaultParaFormat(handleRef); // to apply paragraph formatting

            IntPtr oldMask = BeginUpdate(handleRef);
            SetHideSelectionInternal(handleRef, true);

            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Fragment;
            settings.CheckCharacters = false;

            try
            {
                using (StringReader stringreader = new StringReader(EscapeNonXMLChars(xhtmlText)))
                {
                    XmlReader reader = XmlReader.Create(stringreader, settings);
                    while (reader.Read())
                        ProcessNode(rtb, handleRef, reader, cs);
                }
            }
            catch (System.Xml.XmlException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            // apply links style
            CHARFORMAT ncf = new CHARFORMAT(CFM.LINK, CFE.LINK);
            ncf.cbSize = Marshal.SizeOf(ncf);
            foreach (var pair in cs.links)
            {
                rtb.Select(pair.Key, pair.Value);
                SetCharFormat(handleRef, ncf);
            }
            SetHideSelectionInternal(handleRef, false);
            // reposition to first
            rtb.Select(0, 0);
            EndUpdate(handleRef, oldMask);
            rtb.Invalidate();
        }