public void IsEmptyWhenNew() { this.stack = new Stack(); bool isEmpty = this.stack.IsEmpty; Assert.IsTrue(isEmpty); }
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; } }
public static void SetHtml(this TextBlock textBlock, string text) { // Constants try { FontProperties item = new FontProperties(); item.face = textBlock.FontFamily.Source; item.size = textBlock.FontSize; item.brush = textBlock.Foreground; item.bold = false; item.italic = false; item.underline = false; item.listitem = false; item.listtype = 0; item.listitems = 0; item.indent = 0; item.link = null; g_sFonts = new Stack<FontProperties>(); g_sFonts.Push(item); g_iPrevLineBreaks = 2; if (textBlock.Inlines == null) textBlock.Text = ""; // TextBlock Bug: work around the null inlines // Clear the collection of Inlines textBlock.Inlines.Clear(); // Wrap the input in a <DIV> (so even plain text becomes valid XML) StringReader stringReader = new StringReader(string.Concat("<DIV>", text, "</DIV>")); // Read the input XmlReader xmlReader = XmlReader.Create(stringReader); // Read the entire XML DOM... while (xmlReader.Read()) { string nameLower = xmlReader.Name.ToLower(); if (xmlReader.NodeType == XmlNodeType.Element) { bool bEmpty = xmlReader.IsEmptyElement; // Handle the begin element switch (nameLower) { case "a": { if (bEmpty) break; FontProperties font = g_sFonts.Peek(); if (xmlReader.MoveToFirstAttribute()) do { if (xmlReader.Name.ToLower() == "href") { font.link = xmlReader.Value; font.underline = true; font.brush = Colors.Blue.ToBrush(); } } while (xmlReader.MoveToNextAttribute()); g_sFonts.Push(font); break; } case "font": { if (bEmpty) break; FontProperties font = g_sFonts.Peek(); if (xmlReader.MoveToFirstAttribute()) do { if (xmlReader.Name.ToLower() == "face") font.face = xmlReader.Value; if (xmlReader.Name.ToLower() == "size") font.size = Convert.ToDouble(xmlReader.Value); if (xmlReader.Name.ToLower() == "color") font.brush = xmlReader.Value.ToColor().ToBrush(); } while (xmlReader.MoveToNextAttribute()); g_sFonts.Push(font); break; } case "b": case "strong": { if (bEmpty) break; FontProperties font = g_sFonts.Peek(); font.bold = true; g_sFonts.Push(font); break; } case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); if (bEmpty) break; FontProperties font = g_sFonts.Peek(); if (nameLower == "h1") font.size *= (24.0/12); // xx-large 24pt 32pix if (nameLower == "h2") font.size *= (18.0/12); // x-large 18pt 24pix if (nameLower == "h3") font.size *= (13.5/12); // large 13.5pt 18pix if (nameLower == "h4") font.size *= (12.0/12); // medium 12pt 16pix if (nameLower == "h5") font.size *= (10.5/12); // sSmall 10.5pt 14pix if (nameLower == "h6") font.size *= ( 7.5/12); // x-small 7.5pt 10pix // xx-small 7.5pt 10pix font.bold = true; g_sFonts.Push(font); break; } case "i": case "em": case "cite": { if (bEmpty) break; FontProperties font = g_sFonts.Peek(); font.italic = true; g_sFonts.Push(font); break; } case "u": { if (bEmpty) break; FontProperties font = g_sFonts.Peek(); font.underline = true; g_sFonts.Push(font); break; } case "br": { textBlock.InlinesAdd(new LineBreak()); break; } case "pre": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); if (bEmpty) break; FontProperties font = g_sFonts.Peek(); font.face = "Courier New"; g_sFonts.Push(font); break; } case "p": case "form": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); break; } case "ol": case "ul": case "dir": case "menu": { textBlock.BreakAfterText(); FontProperties font = g_sFonts.Peek(); if (bEmpty || font.listtype == 0) textBlock.StartNewParagraph(); if (bEmpty) break; font.listtype = (nameLower == "ol" ? 1 : 2); font.listitems = 0; font.indent += 3; g_sFonts.Push(font); break; } case "li": { textBlock.BreakAfterText(); if (bEmpty) break; FontProperties font = g_sFonts.Pop(); // Pop not Peek here font.listitems++; g_sFonts.Push(font); font.listitem = true; g_sFonts.Push(font); break; } case "div": case "center": { textBlock.BreakAfterText(); break; } case "span": { break; } } } else if (xmlReader.NodeType == XmlNodeType.EndElement) { // Handle the end element switch (nameLower) { case "a": { g_sFonts.Pop(); break; } case "font": { g_sFonts.Pop(); break; } case "b": case "strong": { g_sFonts.Pop(); break; } case "h1": case "h2": case "h3": case "h4": case "h5": case "h6": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); g_sFonts.Pop(); break; } case "i": case "em": case "cite": { g_sFonts.Pop(); break; } case "u": { g_sFonts.Pop(); break; } case "br": { break; } case "pre": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); g_sFonts.Pop(); break; } case "p": case "form": { textBlock.BreakAfterText(); textBlock.StartNewParagraph(); break; } case "ol": case "ul": case "dir": case "menu": { textBlock.BreakAfterText(); FontProperties font = g_sFonts.Pop(); if (font.listtype == 0) textBlock.StartNewParagraph(); break; } case "li": { textBlock.BreakAfterText(); g_sFonts.Pop(); break; } case "div": case "center": { textBlock.BreakAfterText(); break; } case "span": { break; } } } else if (xmlReader.NodeType == XmlNodeType.Text) { // Create a Run for the visible text // Collapse contiguous whitespace per HTML behavior StringBuilder builder = new StringBuilder(xmlReader.Value.Length); char cLast = (g_iPrevLineBreaks > 0 ? ' ' : '\0'); foreach (char ch in xmlReader.Value) { char c = ch; if (c == '\t' || c == '\n') c = ' '; bool bSkip = (cLast == ' ' && c == ' '); cLast = c; if (!bSkip) builder.Append(c); } // If any text to display... string builderString = builder.ToString(); if (builderString.Length > 0) { // Create a Run to display it Run run = new Run(); run.Text = builderString; textBlock.InlinesAdd(run); } } else if (xmlReader.NodeType == XmlNodeType.Whitespace) { } } } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message); e.Assert(); // Invalid XHTML textBlock.Inlines.Clear(); textBlock.Text = text; } }
public void Setup() { this.stack = new Stack(); }