/// <summary> /// Creates a new top-level document block. /// </summary> internal static Block CreateDocument(PandocMarkSettings settings) { Block e = new Block(BlockTag.Document, 0) { Document = new DocumentData(settings.ExternalReferences) }; e.Top = e; return(e); }
/// <summary>Initializes a new instance of the <see cref="HtmlFormatter" /> class.</summary> /// <param name="target">The target text writer.</param> /// <param name="settings">The settings used when formatting the data.</param> /// <exception cref="ArgumentNullException">when <paramref name="target"/> is <see langword="null"/></exception> public HtmlFormatter(TextWriter target, PandocMarkSettings settings) { if (target is null) { throw new ArgumentNullException(nameof(target)); } if (settings is null) { settings = PandocMarkSettings.Default; } _target = new HtmlTextWriter(target); Settings = settings; }
static AbstractSyntaxTree() { CommonMarkSettings = PandocMarkSettings.Default.Clone(); CommonMarkSettings.TrackSourcePosition = true; }
/// <summary> /// Write the block data to the given writer. /// </summary> public static void PrintBlocks(TextWriter writer, Block block, PandocMarkSettings settings) { int indent = 0; var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); var buffer = new StringBuilder(); var trackPositions = settings.TrackSourcePosition; while (block != null) { writer.Write(new string(' ', indent)); switch (block.Tag) { case BlockTag.Document: writer.Write("document"); PrintPosition(trackPositions, writer, block); break; case BlockTag.BlockQuote: writer.Write("block_quote"); PrintPosition(trackPositions, writer, block); break; case BlockTag.ListItem: writer.Write("list_item"); PrintPosition(trackPositions, writer, block); break; case BlockTag.List: writer.Write("list"); PrintPosition(trackPositions, writer, block); var data = block.ListData; if (data.ListType == ListType.Ordered) { writer.Write(" (type=ordered tight={0} start={1} delim={2})", data.IsTight, data.Start, data.Delimiter); } else { writer.Write("(type=bullet tight={0} bullet_char={1})", data.IsTight, data.BulletChar); } break; case BlockTag.AtxHeading: writer.Write("atx_heading"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.Heading.Level); break; case BlockTag.SetextHeading: writer.Write("setext_heading"); PrintPosition(trackPositions, writer, block); writer.Write(" (level={0})", block.Heading.Level); break; case BlockTag.Paragraph: writer.Write("paragraph"); PrintPosition(trackPositions, writer, block); break; case BlockTag.ThematicBreak: writer.Write("thematic_break"); PrintPosition(trackPositions, writer, block); break; case BlockTag.IndentedCode: writer.Write("indented_code {0}", FormatString(block.StringContent.ToString(buffer), buffer)); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(FormatString(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.FencedCode: writer.Write("fenced_code"); PrintPosition(trackPositions, writer, block); writer.Write(" length={0} info={1} {2}", block.FencedCodeData.FenceLength, FormatString(block.FencedCodeData.Info, buffer), FormatString(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.HtmlBlock: writer.Write("html_block"); PrintPosition(trackPositions, writer, block); writer.Write(' '); writer.Write(FormatString(block.StringContent.ToString(buffer), buffer)); break; case BlockTag.ReferenceDefinition: writer.Write("reference_def"); PrintPosition(trackPositions, writer, block); break; default: throw new PandocMarkException("Block type " + block.Tag + " is not supported.", block); } writer.WriteLine(); if (block.InlineContent != null) { PrintInlines(writer, block.InlineContent, indent + 2, inlineStack, buffer, trackPositions); } if (block.FirstChild != null) { if (block.NextSibling != null) { stack.Push(new BlockStackEntry(indent, block.NextSibling)); } indent += 2; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { var entry = stack.Pop(); indent = entry.Indent; block = entry.Target; } else { block = null; } } }
/// <summary> /// Walk through the block, its children and siblings, parsing string content into inline content where appropriate. /// </summary> /// <param name="block">The document level block from which to start the processing.</param> /// <param name="data">Document data.</param> /// <param name="settings">The settings that influence how the inline parsing is performed.</param> public static void ProcessInlines(Block block, DocumentData data, PandocMarkSettings settings) { Stack <Inline> inlineStack = null; var stack = new Stack <Block>(); var parsers = settings.InlineParsers; var specialCharacters = settings.InlineParserSpecialCharacters; var subj = new Subject(data); StringContent sc; int delta; while (block != null) { var tag = block.Tag; if (tag == BlockTag.Paragraph || tag == BlockTag.AtxHeading || tag == BlockTag.SetextHeading || (tag == BlockTag.HtmlBlock && block.HtmlBlockType == HtmlBlockType.Comment)) { sc = block.StringContent; if (sc != null) { sc.FillSubject(subj); delta = subj.Position; if (tag == BlockTag.HtmlBlock) { block.InlineContent = InlineMethods.ParseHtmlInline(subj); } else { block.InlineContent = InlineMethods.ParseUInline(subj, parsers, specialCharacters); block.StringContent = null; } if (sc.PositionTracker != null) { sc.PositionTracker.AddBlockOffset(-delta); AdjustInlineSourcePosition(block.InlineContent, sc.PositionTracker, ref inlineStack); } } } if (block.FirstChild != null) { if (block.NextSibling != null) { stack.Push(block.NextSibling); } block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { block = stack.Pop(); } else { block = null; } } }
/// <summary> /// Writes the inline list to the given writer as HTML code. /// </summary> private static void InlinesToHtml(HtmlTextWriter writer, Inline inline, PandocMarkSettings 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 { 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: // 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("<del"); if (trackPositions) { PrintPosition(writer, inline); } writer.Write('>'); stackLiteral = "</del>"; 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 PandocMarkException("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 is null && stack.Count > 0) { var entry = stack.Pop(); writer.WriteConstant(entry.Literal); inline = entry.Target; withinLink = entry.IsWithinLink; } } }
private static void BlocksToHtmlInner(HtmlTextWriter writer, Block block, PandocMarkSettings settings) { var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); bool visitChildren; string stackLiteral = null; bool stackTight = false; bool tight = false; bool trackPositions = settings.TrackSourcePosition; int x; while (block != null) { visitChildren = false; switch (block.Tag) { case BlockTag.Document: stackLiteral = null; stackTight = false; visitChildren = true; break; case BlockTag.Paragraph: if (tight) { InlinesToHtml(writer, block.InlineContent, settings, inlineStack); } else { writer.EnsureLine(); writer.WriteConstant("<p"); if (trackPositions) { PrintPosition(writer, block); } writer.Write('>'); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant("</p>"); } break; case BlockTag.BlockQuote: writer.EnsureLine(); writer.WriteConstant("<blockquote"); if (trackPositions) { PrintPosition(writer, block); } writer.WriteLine('>'); stackLiteral = "</blockquote>"; stackTight = false; visitChildren = true; break; case BlockTag.ListItem: writer.EnsureLine(); writer.WriteConstant("<li"); if (trackPositions) { PrintPosition(writer, block); } writer.Write('>'); stackLiteral = "</li>"; stackTight = tight; visitChildren = true; break; case BlockTag.List: // make sure a list starts at the beginning of the line: writer.EnsureLine(); var data = block.ListData; writer.WriteConstant(data.ListType == ListType.Bullet ? "<ul" : "<ol"); if (data.Start != 1) { writer.WriteConstant(" start=\""); writer.WriteConstant(data.Start.ToString(CultureInfo.InvariantCulture)); writer.Write('\"'); } if (trackPositions) { PrintPosition(writer, block); } writer.WriteLine('>'); stackLiteral = data.ListType == ListType.Bullet ? "</ul>" : "</ol>"; stackTight = data.IsTight; visitChildren = true; break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: writer.EnsureLine(); x = block.Heading.Level; if (trackPositions) { writer.WriteConstant("<h" + x.ToString(CultureInfo.InvariantCulture)); PrintPosition(writer, block); writer.Write('>'); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant(x > 0 && x < 7 ? HeadingCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); } else { writer.WriteConstant(x > 0 && x < 7 ? HeadingOpenerTags[x - 1] : "<h" + x.ToString(CultureInfo.InvariantCulture) + ">"); InlinesToHtml(writer, block.InlineContent, settings, inlineStack); writer.WriteLineConstant(x > 0 && x < 7 ? HeadingCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); } break; case BlockTag.IndentedCode: case BlockTag.FencedCode: writer.EnsureLine(); writer.WriteConstant("<pre><code"); if (trackPositions) { PrintPosition(writer, block); } var info = block.FencedCodeData?.Info; if (info != null && info.Length > 0) { x = info.IndexOf(' '); if (x == -1) { x = info.Length; } writer.WriteConstant(" class=\"language-"); EscapeHtml(new StringPart(info, 0, x), writer); writer.Write('\"'); } writer.Write('>'); EscapeHtml(block.StringContent, writer); writer.WriteLineConstant("</code></pre>"); break; case BlockTag.HtmlBlock: // cannot output source position for HTML blocks block.StringContent.WriteTo(writer); break; case BlockTag.ThematicBreak: if (trackPositions) { writer.WriteConstant("<hr"); PrintPosition(writer, block); writer.WriteLine(); } else { writer.WriteLineConstant("<hr />"); } break; case BlockTag.ReferenceDefinition: break; default: throw new PandocMarkException("Block type " + block.Tag + " is not supported.", block); } if (visitChildren) { stack.Push(new BlockStackEntry(stackLiteral, block.NextSibling, tight)); tight = stackTight; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else { block = null; } while (block is null && stack.Count > 0) { var entry = stack.Pop(); if (entry.Literal != null) { writer.WriteLineConstant(entry.Literal); } tight = entry.IsTight; block = entry.Target; } } }
public static void BlocksToHtml(TextWriter writer, Block block, PandocMarkSettings settings) { var wrapper = new HtmlTextWriter(writer); BlocksToHtmlInner(wrapper, block, settings); }