/// <summary> /// Writes the specified inline element to the output stream. Does not write the child nodes, instead /// the <paramref name="ignoreChildNodes"/> is used to notify the caller whether it should recurse /// into the child nodes. /// </summary> /// <param name="inline">The inline element to be written to the output stream.</param> /// <param name="isOpening">Specifies whether the inline element is being opened (or started).</param> /// <param name="isClosing">Specifies whether the inline element is being closed. If the inline does not /// have child nodes, then both <paramref name="isClosing"/> and <paramref name="isOpening"/> can be /// <see langword="true"/> at the same time.</param> /// <param name="ignoreChildNodes">Instructs the caller whether to skip processing of child nodes or not.</param> protected virtual void WriteInline(Inline inline, bool isOpening, bool isClosing, out bool ignoreChildNodes) { if (RenderPlainTextInlines.Peek()) { switch (inline.Tag) { case InlineTag.String: case InlineTag.Code: case InlineTag.RawHtml: WriteEncodedHtml(inline.LiteralContentValue); break; case InlineTag.LineBreak: case InlineTag.SoftBreak: WriteLine(); break; case InlineTag.Image: if (isOpening) { RenderPlainTextInlines.Push(true); } if (isClosing) { RenderPlainTextInlines.Pop(); if (!RenderPlainTextInlines.Peek()) { goto useFullRendering; } } break; case InlineTag.Link: case InlineTag.Strong: case InlineTag.Emphasis: case InlineTag.Strikethrough: case InlineTag.Placeholder: break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } ignoreChildNodes = false; return; } useFullRendering: switch (inline.Tag) { case InlineTag.String: ignoreChildNodes = true; if (Settings.TrackSourcePosition) { Write("<span"); WritePositionAttribute(inline); Write('>'); WriteEncodedHtml(inline.LiteralContentValue); Write("</span>"); } else { WriteEncodedHtml(inline.LiteralContentValue); } break; case InlineTag.LineBreak: ignoreChildNodes = true; WriteLine("<br />"); break; case InlineTag.SoftBreak: ignoreChildNodes = true; if (Settings.RenderSoftLineBreaksAsLineBreaks) { WriteLine("<br />"); } else { if (Settings.RenderEmptyLines) { _target.WriteEmptyLine(); } else { WriteLine(); } } break; case InlineTag.Code: ignoreChildNodes = true; Write("<code"); if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write('>'); WriteEncodedHtml(inline.LiteralContentValue); Write("</code>"); break; case InlineTag.RawHtml: ignoreChildNodes = true; if (Settings.HtmlEntityEncode) { WriteEncodedHtml(inline.LiteralContentValue); } else { // cannot output source position for HTML blocks Write(inline.LiteralContentValue); } break; case InlineTag.Link: ignoreChildNodes = false; if (isOpening) { Write("<a href=\""); var uriResolver = Settings.UriResolver; if (uriResolver != null) { WriteEncodedUrl(uriResolver(inline.TargetUrl)); } else { WriteEncodedUrl(inline.TargetUrl); } Write('\"'); if (inline.LiteralContentValue.Length > 0) { Write(" title=\""); WriteEncodedHtml(inline.LiteralContentValue); Write('\"'); } if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write('>'); } if (isClosing) { Write("</a>"); } break; case InlineTag.Image: ignoreChildNodes = false; if (isOpening) { Write("<img src=\""); var uriResolver = Settings.UriResolver; if (uriResolver != null) { WriteEncodedUrl(uriResolver(inline.TargetUrl)); } else { WriteEncodedUrl(inline.TargetUrl); } Write("\" alt=\""); if (!isClosing) { RenderPlainTextInlines.Push(true); } } if (isClosing) { // this.RenderPlainTextInlines.Pop() is done by the plain text renderer above. Write('\"'); if (inline.LiteralContentValue.Length > 0) { Write(" title=\""); WriteEncodedHtml(inline.LiteralContentValue); Write('\"'); } if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write(" />"); } break; case InlineTag.Strong: ignoreChildNodes = false; if (isOpening) { Write("<strong"); if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write('>'); } if (isClosing) { Write("</strong>"); } break; case InlineTag.Emphasis: ignoreChildNodes = false; if (isOpening) { Write("<em"); if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write('>'); } if (isClosing) { Write("</em>"); } break; case InlineTag.Strikethrough: ignoreChildNodes = false; if (isOpening) { Write("<s"); if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write('>'); } if (isClosing) { Write("</s>"); } break; case InlineTag.Placeholder: ignoreChildNodes = false; if (isOpening) { string placeholderSubstitute = null; try { placeholderSubstitute = (_placeholderResolver != null) ? _placeholderResolver(inline.TargetUrl) : null; } catch (Exception ex) { throw new CommonMarkException("An error occurred while resolving a placeholder.", ex); } if (placeholderSubstitute != null) { ignoreChildNodes = true; if (Settings.TrackSourcePosition) { WritePositionAttribute(inline); } Write(placeholderSubstitute); _endPlaceholders.Push('\0'); } else { ignoreChildNodes = false; Write("["); _endPlaceholders.Push(']'); } } if (isClosing) { var closingChar = _endPlaceholders.Pop(); if (closingChar != '\0') { Write(closingChar); } } break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } }
/// <summary> /// Writes the inline list to the given writer as HTML code. /// </summary> private static void InlinesToHtml(HtmlTextWriter writer, Inline inline, CommonMarkSettings settings, Stack <InlineStackEntry> stack) { var uriResolver = settings.UriResolver; bool withinLink = false; bool stackWithinLink = false; bool visitChildren; bool trackPositions = settings.TrackSourcePosition; string stackLiteral = null; while (inline != null) { visitChildren = false; switch (inline.Tag) { case InlineTag.String: if (trackPositions) { writer.WriteConstant("<span"); PrintPosition(writer, inline); writer.Write('>'); EscapeHtml(inline.LiteralContentValue, writer); writer.WriteConstant("</span>"); } else { EscapeHtml(inline.LiteralContentValue, writer); } break; case InlineTag.LineBreak: writer.WriteLineConstant("<br />"); break; case InlineTag.SoftBreak: if (settings.RenderSoftLineBreaksAsLineBreaks) { writer.WriteLineConstant("<br />"); } else { if (settings.RenderEmptyLines) { writer.WriteEmptyLine(); } else { writer.WriteLine(); } } break; case InlineTag.Code: writer.WriteConstant("<code"); if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); EscapeHtml(inline.LiteralContentValue, writer); writer.WriteConstant("</code>"); break; case InlineTag.RawHtml: if (settings.HtmlEntityEncode) { EscapeHtml(inline.LiteralContentValue, writer); } else { // cannot output source position for HTML blocks writer.Write(inline.LiteralContentValue); } break; case InlineTag.Link: if (withinLink) { writer.Write('['); stackLiteral = "]"; stackWithinLink = true; visitChildren = true; } else { writer.WriteConstant("<a href=\""); if (uriResolver != null) { EscapeUrl(uriResolver(inline.TargetUrl), writer); } else { EscapeUrl(inline.TargetUrl, writer); } writer.Write('\"'); if (inline.LiteralContentValue.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContentValue, writer); writer.Write('\"'); } if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); visitChildren = true; stackWithinLink = true; stackLiteral = "</a>"; } break; case InlineTag.Image: writer.WriteConstant("<img src=\""); if (uriResolver != null) { EscapeUrl(uriResolver(inline.TargetUrl), writer); } else { EscapeUrl(inline.TargetUrl, writer); } writer.WriteConstant("\" alt=\""); InlinesToPlainText(writer, inline.FirstChild, stack); writer.Write('\"'); if (inline.LiteralContentValue.Length > 0) { writer.WriteConstant(" title=\""); EscapeHtml(inline.LiteralContentValue, writer); writer.Write('\"'); } if (trackPositions) { PrintPosition(writer, inline); } writer.WriteConstant(" />"); break; case InlineTag.Strong: writer.WriteConstant("<strong"); if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); stackLiteral = "</strong>"; stackWithinLink = withinLink; visitChildren = true; break; case InlineTag.Emphasis: writer.WriteConstant("<em"); if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); stackLiteral = "</em>"; visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Strikethrough: writer.WriteConstant("<s"); if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); stackLiteral = "</s>"; visitChildren = true; stackWithinLink = withinLink; break; case InlineTag.Placeholder: // the slim formatter will treat placeholders like literals, without applying any further processing writer.Write('['); if (trackPositions) { PrintPosition(writer, inline); } stackLiteral = "]"; stackWithinLink = withinLink; visitChildren = true; break; default: throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline); } if (visitChildren) { stack.Push(new InlineStackEntry(stackLiteral, inline.NextSibling, withinLink)); withinLink = stackWithinLink; inline = inline.FirstChild; } else if (inline.NextSibling != null) { inline = inline.NextSibling; } else { inline = null; } while (inline == null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteConstant(entry.Literal); inline = entry.Target; withinLink = entry.IsWithinLink; } } }