internal static CssSelectors Create(string selector) { CssSelectors selectors = new CssSelectors(); selectors.AddFromText(selector); return(selectors); }
internal void SetHtml(FrameworkElement rootElement, string htmlText, Control optionalFontTemplate) { try { // This is the main entry point, initialize everything _RootElement = rootElement; if (!(_RootElement is Panel || _RootElement is RichTextBox)) { return; } if (_RootElement is Panel) { _CurrentRichTextBox = new RichTextBlock(); (_RootElement as Panel).Children.Clear(); (_RootElement as Panel).Children.Add(_CurrentRichTextBox); } else if (_RootElement is RichTextBox) { _CurrentRichTextBox = _RootElement as RichTextBox; } _CurrentParagraph = new Paragraph(); _CurrentRichTextBox.Blocks.Add(_CurrentParagraph); // Setup the initial document state DocumentState documentState = new DocumentState(); if (optionalFontTemplate == null) { optionalFontTemplate = _RootElement as Control; } if (optionalFontTemplate != null) { documentState.Brush = optionalFontTemplate.Foreground; documentState.Face = optionalFontTemplate.FontFamily.Source; documentState.Size = optionalFontTemplate.FontSize; documentState.Italic = (optionalFontTemplate.FontStyle == FontStyles.Italic); documentState.Bold = (optionalFontTemplate.FontWeight != FontWeights.Normal); documentState.Underline = false; } else { Run defaultRun = new Run(); documentState.Brush = defaultRun.Foreground; documentState.Face = defaultRun.FontFamily.Source; documentState.Size = defaultRun.FontSize; documentState.Italic = (defaultRun.FontStyle == FontStyles.Italic); documentState.Bold = (defaultRun.FontWeight != FontWeights.Normal); documentState.Underline = (defaultRun.TextDecorations == TextDecorations.Underline); } _DocumentStates = new Stack <DocumentState>(); _DocumentStates.Push(documentState); _PriorLineBreaks = 2; // As if we are following a paragraph bool hasMarkup = (htmlText != null && htmlText.IndexOf("<") >= 0 && htmlText.IndexOf(">") > 0); if (!hasMarkup) // translate ampersands & and the like { htmlText = HttpUtility.HtmlEncode(htmlText); } bool hasXmlDirective = (hasMarkup && htmlText.IndexOf("<?xml") >= 0); if (!hasXmlDirective) // add an outer <span> to ensure valid XML regardless of the text markup { htmlText = string.Concat("<span>", htmlText, "</span>"); } bool hasInvalidEntity = (htmlText.IndexOf(" ") >= 0); if (hasInvalidEntity) { htmlText = htmlText.Replace(" ", " "); } hasInvalidEntity = (htmlText.IndexOf("©") >= 0); if (hasInvalidEntity) { htmlText = htmlText.Replace("©", "©"); } // Read the XML DOM input StringReader stringReader = new StringReader(htmlText); XmlReaderSettings settings = new XmlReaderSettings(); settings.DtdProcessing = DtdProcessing.Ignore; settings.IgnoreWhitespace = false; settings.IgnoreProcessingInstructions = true; settings.IgnoreComments = true; //settings.CheckCharacters = false; //settings.ConformanceLevel = ConformanceLevel.Auto; XmlReader xmlReader = XmlReader.Create(stringReader, settings); while (xmlReader.Read()) { string nameLower = xmlReader.Name.ToLower(); if (xmlReader.NodeType == XmlNodeType.Element) { // Process the element start bool bEmpty = xmlReader.IsEmptyElement; switch (nameLower) { // Process the following elements: // "style" // "a" "img" "font" // "b" "strong" // "h1" "h2" "h3" "h4" "h5" "h6" // "i" "em" "cite" // "u" "br" "pre" "code" "tt" // "p" "form" // "ol" "ul" "blockquote" "dir" "menu" // "li" "div" "center" "span" case "head": case "object": case "meta": case "title": case "script": case "noscript": { _IgnoreText = true; break; } case "style": { _InternalStyles = string.Empty; break; } case "link": { string rel = null; string href = null; if (xmlReader.MoveToFirstAttribute()) { do { if (xmlReader.Name.ToLower() == "rel") { rel = xmlReader.Value; } else if (xmlReader.Name.ToLower() == "href") { href = xmlReader.Value; } }while (xmlReader.MoveToNextAttribute()); } if (rel == "stylesheet" && href != null) { if (_DocumentUri != null) { href = _DocumentUri.Site().Append(href).ToString(); } //j CssParser.DownloadStyles(UriHelper.MakeAbsolute(_DocumentUri.Site(), href), ref _GlobalStyles); } break; } case "a": case "area": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); string href = null; if (xmlReader.MoveToFirstAttribute()) { do { if (xmlReader.Name.ToLower() == "href") { href = xmlReader.Value; } }while (xmlReader.MoveToNextAttribute()); } if (href != null) { if (_DocumentUri != null && !href.StartsWith("#")) { href = _DocumentUri.Site().Append(href).ToString(); } state.Href = href; } _DocumentStates.Push(state); break; } case "base": { string href = null; if (xmlReader.MoveToFirstAttribute()) { do { if (xmlReader.Name.ToLower() == "href") { href = xmlReader.Value; } }while (xmlReader.MoveToNextAttribute()); } if (href != null) { _DocumentUri = new Uri(href, UriKind.Absolute); } break; } case "img": { string source = null; DocumentState state = _DocumentStates.Peek(); if (xmlReader.MoveToFirstAttribute()) { do { if (xmlReader.Name.ToLower() == "src") { source = xmlReader.Value; } }while (xmlReader.MoveToNextAttribute()); } if (source != null) { if (_DocumentUri != null) { source = _DocumentUri.Site().Append(source).ToString(); } AddImage(source); } break; } case "font": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); if (xmlReader.MoveToFirstAttribute()) { do { if (xmlReader.Name.ToLower() == "face") { state.Face = xmlReader.Value; } else if (xmlReader.Name.ToLower() == "size") { state.Size = CssParser.ParseFontSize(xmlReader.Value, state.Size); } else if (xmlReader.Name.ToLower() == "color") { state.Brush = xmlReader.Value.ToColor().ToBrush(); } }while (xmlReader.MoveToNextAttribute()); } _DocumentStates.Push(state); break; } case "big": case "small": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); double percent = (nameLower == "big" ? 1.2 : .8); state.Size *= percent; _DocumentStates.Push(state); break; } case "b": case "strong": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); state.Bold = true; _DocumentStates.Push(state); break; } case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": { MoveToBeginLine(true); if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); // Special h? font size handling if (!ProcessCommonStyles(ref state, xmlReader, nameLower)) { state.Size = CssParser.ParseFontSize(nameLower, state.Size); } state.Bold = true; _DocumentStates.Push(state); break; } case "i": case "em": case "cite": case "address": case "dfn": // Definition term case "var": // Variable { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); state.Italic = true; _DocumentStates.Push(state); break; } case "u": case "ins": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); state.Underline = true; _DocumentStates.Push(state); break; } case "s": case "strike": case "del": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); //state.StrikeThrough = true; _DocumentStates.Push(state); break; } case "br": { AddLineBreak(); break; } case "pre": case "code": case "samp": // Sample computer code case "kbd": case "tt": { if (nameLower == "pre") { MoveToBeginLine(true); } if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); state.Face = "Courier New"; _DocumentStates.Push(state); break; } case "ol": case "ul": case "dir": // Same as "ul" case "menu": // Same as "ul" { DocumentState state = _DocumentStates.Peek(); bool newParagraph = (bEmpty || state.BulletType == '\0'); MoveToBeginLine(newParagraph); if (bEmpty) { break; } ProcessCommonStyles(ref state, xmlReader, nameLower); state.BulletType = (nameLower == "ol" ? 'o' : 'u'); state.ListItemNumber = 0; state.Indent += 8; _DocumentStates.Push(state); break; } case "li": { MoveToBeginLine(false); // Bump the list item number DocumentState state = _DocumentStates.Pop(); state.ListItemNumber++; _DocumentStates.Push(state); //DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); AddIndent(); AddListItem(); break; } case "blockquote": { MoveToBeginLine(true); if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); state.Indent += 8; _DocumentStates.Push(state); break; } case "div": case "p": case "body": case "form": case "center": case "textarea": { MoveToBeginLine(true); if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } case "table": case "caption": case "tr": case "td": { if (nameLower != "td") { MoveToBeginLine(false); } if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } case "sup": case "sub": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } case "dl": case "dt": case "dd": { bool newParagraph = (nameLower == "dl"); MoveToBeginLine(newParagraph); if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); if (nameLower == "dd") { state.Indent += 8; } ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } case "span": case "label": case "q": case "abbr": case "acronym": { if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } case "legend": { MoveToBeginLine(false); if (bEmpty) { break; } DocumentState state = _DocumentStates.Peek(); ProcessCommonStyles(ref state, xmlReader, nameLower); _DocumentStates.Push(state); break; } } } else if (xmlReader.NodeType == XmlNodeType.EndElement) { // Process the element end switch (nameLower) { case "head": case "object": case "meta": case "title": case "script": case "noscript": { _IgnoreText = false; break; } case "style": { _GlobalSelectors = CssSelectors.Create(_InternalStyles); _InternalStyles = null; break; } case "link": { _IgnoreText = false; break; } case "a": case "area": { _DocumentStates.Pop(); break; } case "base": { break; } case "img": { break; } case "font": { _DocumentStates.Pop(); break; } case "big": case "small": { _DocumentStates.Pop(); break; } case "b": case "strong": { _DocumentStates.Pop(); break; } case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": { MoveToBeginLine(true); _DocumentStates.Pop(); break; } case "i": case "em": case "cite": case "address": case "dfn": // Definition term case "var": // Variable { _DocumentStates.Pop(); break; } case "u": case "ins": { _DocumentStates.Pop(); break; } case "s": case "strike": case "del": { _DocumentStates.Pop(); break; } case "br": { break; } case "pre": case "code": case "samp": // Sample computer code case "kbd": case "tt": { if (nameLower == "pre") { MoveToBeginLine(true); } _DocumentStates.Pop(); break; } case "ol": case "ul": case "dir": case "menu": { MoveToBeginLine(true); _DocumentStates.Pop(); break; } case "li": { MoveToBeginLine(false); _DocumentStates.Pop(); break; } case "blockquote": { MoveToBeginLine(true); _DocumentStates.Pop(); break; } case "div": case "p": case "body": case "form": case "center": case "textarea": { MoveToBeginLine(false); _DocumentStates.Pop(); break; } case "table": case "caption": case "tr": case "td": { if (nameLower != "td") { MoveToBeginLine(false); } _DocumentStates.Pop(); break; } case "sup": case "sub": { _DocumentStates.Pop(); break; } case "dl": case "dt": case "dd": { bool newParagraph = (nameLower == "dl"); MoveToBeginLine(newParagraph); _DocumentStates.Pop(); break; } case "span": case "label": case "q": case "abbr": case "acronym": { _DocumentStates.Pop(); break; } case "legend": { MoveToBeginLine(false); _DocumentStates.Pop(); break; } } } else if (xmlReader.NodeType == XmlNodeType.Text) { // Process the element text string text = ""; try { text = xmlReader.Value; } catch (Exception ex) { text = ex.Message; } if (_InternalStyles != null) { _InternalStyles += text; } else if (!_IgnoreText) { // Remove redundant whitespace ala HTML StringBuilder builder = new StringBuilder(text.Length); char cLast = (_PriorLineBreaks > 0 ? ' ' : '\0'); foreach (char ch in text) { char c = ch; if (c == '\t' || c == '\n') { c = ' '; } bool bSkip = (cLast == ' ' && c == ' '); cLast = c; if (!bSkip) { builder.Append(c); } } // Output the text string textRun = builder.ToString(); AddText(textRun); } } else if (xmlReader.NodeType == XmlNodeType.Whitespace) { // Process the element whitespace if (_InternalStyles != null) { _InternalStyles += " "; } else if (!_IgnoreText) { // Remove redundant whitespace ala HTML bool bSkip = (_PriorLineBreaks > 0); if (!bSkip) { AddText(" "); } } } } FlushAll(); } catch (Exception ex) { //ex.DebugOutput(); ex.Alert(); // Invalid XHTML; Clear any existing collection of Inlines if (_RootElement is RichTextBox) { (_RootElement as RichTextBox).Blocks.Clear(); Paragraph paragraph = new Paragraph(); paragraph.Inlines.Add(new Run() { Text = htmlText }); (_RootElement as RichTextBox).Blocks.Add(paragraph); } } finally { _DocumentStates.Clear(); _DocumentStates = null; if (_GlobalSelectors != null) { _GlobalSelectors.Dispose(); _GlobalSelectors = null; } _RootElement = null; _CurrentRichTextBox = null; _CurrentParagraph = null; } }