public string ConvertToHtml(string markDownText, string filepath) { var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); pipeline.PreciseSourceLocation = true; try { htmlRenderer.BaseUrl = new Uri(filepath); } catch (Exception e) { } sb.Clear(); var document = Markdown.Parse(markDownText, pipeline, null); SetLineNoAttributeOnAllBlocks(document); pipeline.Setup(htmlRenderer); htmlRenderer.Render(document); htmlWriter.Flush(); var result = sb.ToString(); return(result); }
public static (Dictionary <string, string> Meta, string Content) ParseDemoDoc(string input) { var pipeline = new MarkdownPipelineBuilder() .UseYamlFrontMatter() .Build(); StringWriter writer = new StringWriter(); var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); MarkdownDocument document = Markdown.Parse(input, pipeline); var yamlBlock = document.Descendants <YamlFrontMatterBlock>().FirstOrDefault(); Dictionary <string, string> meta = null; if (yamlBlock != null) { string yaml = input.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length).Trim('-'); meta = new Deserializer().Deserialize <Dictionary <string, string> >(yaml); } renderer.Render(document); writer.Flush(); string html = writer.ToString(); return(meta, html); }
public static (Dictionary <string, string> Meta, string Desc, string ApiDoc) ParseDemoDoc(string input) { var pipeline = new MarkdownPipelineBuilder() .UseYamlFrontMatter() .UsePipeTables() .Build(); var document = Markdown.Parse(input, pipeline); var yamlBlock = document.Descendants <YamlFrontMatterBlock>().FirstOrDefault(); Dictionary <string, string> meta = null; if (yamlBlock != null) { var yaml = input.Substring(yamlBlock.Span.Start, yamlBlock.Span.Length).Trim('-'); meta = new Deserializer().Deserialize <Dictionary <string, string> >(yaml); } var isAfterApi = false; var descPart = ""; var apiPart = ""; for (var i = yamlBlock?.Line ?? 0; i < document.Count; i++) { var block = document[i]; if (block is YamlFrontMatterBlock) { continue; } if (block is HeadingBlock heading && heading.Level == 2 && heading.Inline.FirstChild.ToString() == "API") { isAfterApi = true; } using var writer = new StringWriter(); var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); var blockHtml = renderer.Render(block); if (!isAfterApi) { descPart += blockHtml; } else { apiPart += blockHtml; } } return(meta, descPart, apiPart); }
protected string GetMarkdownRendererFromMarkdownDocument(MarkdownDocument document) { var pipeline = new MarkdownPipelineBuilder().Build(); var writer = new StringWriter(); var renderer = new Markdig.Renderers.HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(document); writer.Flush(); return(writer.ToString()); }
private static T UseMarkdown <T>(this T kernel) where T : Kernel { var pipeline = new MarkdownPipelineBuilder() .UseMathematics() .UseAdvancedExtensions() .Build(); kernel.AddDirective(new Command("#!markdown", "Convert the code that follows from Markdown into HTML") { Handler = CommandHandler.Create((InvocationContext cmdLineContext) => { var context = cmdLineContext.GetService <KernelInvocationContext>(); if (context.Command is SubmitCode submitCode) { var markdown = submitCode.Code .Replace("#!markdown", "") .Trim(); var document = Markdown.Parse( markdown, pipeline); string html; using (var writer = new StringWriter()) { var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(document); html = writer.ToString(); } context.Publish( new DisplayedValueProduced( html, context.Command, new[] { new FormattedValue("text/html", html) })); context.Complete(submitCode); } return(Task.CompletedTask); }) });
/// <summary> /// Render the Markdown data to generic HTML. /// </summary> /// <param name="markup">The Markdown to mark up.</param> /// <returns>Generic HTML markup.</returns> private static string Render(string markup) { var sb = new StringBuilder(); var sw = new StringWriter(sb); var render = new HtmlRenderer(sw); var pipeline = new MarkdownPipelineBuilder() .Build(); pipeline.Setup(render); var doc = MarkdownParser.Parse(markup); render.Render(doc); sw.Flush(); return(sb.ToString()); }
static string ToMarkdown(IEnumerable <Block> blocks) { var writer = new StringWriter(); var renderer = new NormalizeRenderer(writer); var pipeline = new MarkdownPipelineBuilder().Build(); pipeline.Setup(renderer); foreach (var block in blocks) { renderer.Render(block); } // We convert \n to \r because the YAML serialization will eventually // output \n\n for \n, but \r\n for \r. return(writer.ToString().TrimEnd().Replace('\n', '\r')); }
public void Test3() { var pipeline = new MarkdownPipelineBuilder() .Use <VariableExtension>() .Build(); var doc = MarkdownParser.Parse("# Hallo\r\n## Test\r\n$(Test)\r\nTest", pipeline); var adf = new AdfDocument(); var renderer = new AdfRenderer(adf); pipeline.Setup(renderer); renderer.Render(doc); }
public void CorrectlyIdentifiesParserMatch(string markdown) { var _pipeline = new MarkdownPipelineBuilder() .UseEmbeddedGists(config => { config.UseMockRender(_renderFragment); config.AddBaseUrl(_alternateBaseUrl); }).Build(); _pipeline.Setup(_renderer); var _result = Markdown.ToHtml(markdown, _pipeline); _result.Trim().Should().Be($"<p>{_renderFragment}</p>"); }
// notes may contain html and markdown, so to include: // 1. Convert html into markdown // 2. Parse markdown into ast // 3. Normalize headings // 4. Convert ast into markdown text // 5. Add markdown text to stream public string Normalize(string text) { string markdownText = _converter.Convert(text); MarkdownDocument markdown = Markdown.Parse(markdownText); using (var writer = new StringWriter()) { var pipeline = new MarkdownPipelineBuilder().Build(); pipeline.Extensions.AddIfNotAlready <SoftlineBreakAsHardlineExtension>(); var renderer = new NormalizeRenderer(writer); pipeline.Setup(renderer); renderer.Render(markdown); writer.Flush(); return(writer.ToString()); } }
private static async Task <string> Render( PublishFormat format, MarkdownDocument document, Dictionary <string, string> outputsBySessionName) { MarkdownPipeline pipeline; IMarkdownRenderer renderer; var writer = new StringWriter(); switch (format) { case PublishFormat.Markdown: pipeline = new MarkdownPipelineBuilder() .UseNormalizeCodeBlockAnnotations(outputsBySessionName) .Build(); var normalizeRenderer = new NormalizeRenderer(writer); normalizeRenderer.Writer.NewLine = "\n"; renderer = normalizeRenderer; break; case PublishFormat.HTML: pipeline = new MarkdownPipelineBuilder() .UseCodeBlockAnnotations(inlineControls: false) .Build(); renderer = new HtmlRenderer(writer); break; default: throw new ArgumentOutOfRangeException(nameof(format), format, null); } pipeline.Setup(renderer); var blocks = document .OfType <AnnotatedCodeBlock>() .OrderBy(c => c.Order) .ToList(); await Task.WhenAll(blocks.Select(b => b.InitializeAsync())); renderer.Render(document); writer.Flush(); var rendered = writer.ToString(); return(rendered); }
public static void TestSpec(Func <string, string> linkRewriter, string markdown, string expectedLink) { var pipeline = new MarkdownPipelineBuilder().Build(); var writer = new StringWriter(); var renderer = new HtmlRenderer(writer); renderer.LinkRewriter = linkRewriter; pipeline.Setup(renderer); var document = MarkdownParser.Parse(markdown, pipeline); renderer.Render(document); writer.Flush(); Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\"")); }
public void CorrectlyIgnoresStandardLink(string markdown) { var _pipeline = new MarkdownPipelineBuilder() .UseAutoLinks() .UseEmbeddedGists(config => { config.UseMockRender(_renderFragment); config.AddBaseUrl(_alternateBaseUrl); }).Build(); _pipeline.Setup(_renderer); var _result = Markdown.ToHtml(markdown, _pipeline); _result.Trim().Should().NotBe($"<p>{_renderFragment}</p>"); _result.Trim().Should().NotContain(_renderFragment); }
protected string ToPlainText(string markdown) { var writer = new StringWriter(); var pipeline = new MarkdownPipelineBuilder().Build(); // We override the renderer with our own writer var renderer = new PlainTextRenderer(writer); pipeline.Setup(renderer); var document = Markdown.Parse(markdown, pipeline); renderer.Render(document); writer.Flush(); return(writer.ToString()); }
/* * public static Item ToItem(string md) * { * var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build(); * var document = MarkdownParser.Parse(md, pipeline); * * var enumerator = document.GetEnumerator(); * try * { * enumerator.MoveNext(); * while (enumerator.Current != null) * { * var block = enumerator.Current; * * if (block is HtmlBlock) * { * if (block.IsNewItem()) * { * var item = ParseItem(ref enumerator); * return item; * } * } * enumerator.MoveNext(); * } * * } * finally * { * enumerator.Dispose(); * } * return null; * } * * public static Item ParseItem(ref ContainerBlock.Enumerator enumerator) * { * var currentItem = enumerator.Current.GetNewItem(); * * if (currentItem != null) * { * enumerator.MoveNext(); * while (enumerator.Current != null) * { * var block = enumerator.Current; * * if (block is HtmlBlock) * { * if (block.IsClosingItem()) * { * return currentItem; * } * else if (block.IsNewItem()) * { * var subItem = ParseItem(ref enumerator); * * var propertyName = subItem.GetType().Name; * * if (currentItem.GetType().GetProperty(propertyName) != null) * { * PropertyInfo prop = currentItem.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); * if (null != prop && prop.CanWrite) * { * prop.SetValue(currentItem, subItem, null); * } * } * else if (currentItem is Items) * { * var items = currentItem as Items; * items.Add(subItem); * } * } * } * * else // if (block is ContainerBlock) * { * ParseItemProperties(currentItem, block); * } * * currentItem.Markdown += enumerator.Current.ToMarkdownString(); * * enumerator.MoveNext(); * } * } * * return currentItem; * } * * public static void ParseItemProperties(Item item, Block block) * { * switch(block) * { * case Markdig.Extensions.Tables.Table table: * ParseItemProperties(item, table); * break; * case ContainerBlock blocks: * ParseItemProperties(item, blocks); * break; * case LeafBlock leaf: * ParseItemProperties(item, leaf.Inline); * break; * } * } * * public static void ParseItemProperties(Item item, ContainerBlock blocks) * { * foreach(var block in blocks) * { * ParseItemProperties(item, block); * } * } * * public static void ParseItemProperties(Item item, ContainerInline inlines) * { * if(inlines == null) * { * return; * } * PropertyInfo prop = null; * foreach (var inline in inlines) * { * if(inline is HtmlInline) * { * var tag = (inline as HtmlInline).Tag; * if(tag == "<!--br-->" || tag =="<br>") * { * * } * else if (tag.StartsWith("<!--/")) * { * prop = null; * } * else if (tag.StartsWith("<!--") && !tag.StartsWith("<!--/")) * { * var propertyName = tag.Substring(4, tag.Length - 7); * prop = item.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); * } * } * else * { * if (null != prop && prop.CanWrite) * { * prop.SetValue(item, inline.ToMarkdownString(), null); * } * } * } * } * * * * public static bool IsNewItem(this Block block) * { * var htmlBlock = block as HtmlBlock; * if (htmlBlock.Type == HtmlBlockType.Comment) * { * var tag = htmlBlock.Lines.Lines.FirstOrDefault().Slice.ToString(); * if (!string.IsNullOrEmpty(tag) && tag != "<!--br-->" && tag != "<br>") * { * if (tag.StartsWith("<!--") && !tag.StartsWith("<!--/")) * { * return true; * } * } * } * return false; * } * * public static bool IsClosingItem(this Block block) * { * var htmlBlock = block as HtmlBlock; * if (htmlBlock.Type == HtmlBlockType.Comment) * { * var tag = htmlBlock.Lines.Lines.FirstOrDefault().Slice.ToString(); * if (!string.IsNullOrEmpty(tag) && tag != "<!--br-->" && tag != "<br>") * { * if (tag.StartsWith("<!--/")) * { * return true; * } * } * } * return false; * } * * public static Item GetNewItem(this Block block) * { * var htmlBlock = block as HtmlBlock; * if (htmlBlock.Type == HtmlBlockType.Comment) * { * var tag = htmlBlock.Lines.Lines.FirstOrDefault().Slice.ToString(); * if (!string.IsNullOrEmpty(tag) && tag != "<!--br-->" && tag != "<br>") * { * if (tag.StartsWith("<!--") && !tag.StartsWith("<!--/")) * { * var name = $"AideDeJeuLib.{tag.Substring(4, tag.Length - 7)}, AideDeJeu"; * var type = Type.GetType(name); * var instance = Activator.CreateInstance(type) as Item; * return instance; * } * } * } * return null; * } */ /* * public static Item ToItem(string md) * { * var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build(); * var document = MarkdownParser.Parse(md, pipeline); * * var enumerator = document.GetEnumerator(); * try * { * enumerator.MoveNext(); * while (enumerator.Current != null) * { * var block = enumerator.Current; * * if (enumerator.Current is ParagraphBlock) * { * if(block.IsNewItem()) * { * var item = block.GetNewItem(); * item.Parse(ref enumerator); * return item; * } * } * enumerator.MoveNext(); * } * * } * finally * { * enumerator.Dispose(); * } * return null; * } * * public static bool IsNewItem(this Block block) * { * var paragraphBlock = block as ParagraphBlock; * var linkInline = paragraphBlock?.Inline?.FirstChild as LinkInline; * if (linkInline != null) * { * var label = linkInline.Label; * var title = linkInline.Title; * if (title == string.Empty && label != string.Empty) * { * return true; * } * } * return false; * } * * public static bool IsClosingItem(this Block block) * { * var paragraphBlock = block as ParagraphBlock; * var linkInline = paragraphBlock?.Inline?.FirstChild as LinkInline; * if (linkInline != null) * { * var label = linkInline.Label; * var title = linkInline.Title; * if (title == string.Empty && label == string.Empty) * { * return true; * } * } * return false; * } * * public static Item GetNewItem(this Block block) * { * var paragraphBlock = block as ParagraphBlock; * var linkInline = paragraphBlock?.Inline?.FirstChild as LinkInline; * if (linkInline != null) * { * var label = linkInline.Label; * var title = linkInline.Title; * var url = linkInline.Url; * if (title == string.Empty) * { * var name = $"AideDeJeuLib.{label}, AideDeJeu"; * var type = Type.GetType(name); * var instance = Activator.CreateInstance(type) as Item; * return instance; * } * } * return null; * } */ public static string ToMarkdownString(this Block block) { var pipeline = new MarkdownPipelineBuilder() .UsePipeTables() .Build(); using (var writer = new StringWriter()) { var renderer = new NormalizeRenderer(writer); renderer.ObjectRenderers.Remove(renderer.ObjectRenderers.FirstOrDefault(i => i is LinkInlineRenderer)); renderer.ObjectRenderers.Add(new LinkInlineRendererEx()); renderer.ObjectRenderers.Add(new TableRenderer()); pipeline.Setup(renderer); renderer.Render(block); return(writer.ToString()); } }
private void Convert(FileInfo mdFile) { var name = mdFile.Directory.Name; var htmlDir = Path.Combine(_config.OutputDirectory, name); if (!Directory.Exists(htmlDir)) { Directory.CreateDirectory(htmlDir); } using (var reader = mdFile.OpenText()) { var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseYamlFrontMatter().Build(); var doc = Markdown.Parse(reader.ReadToEnd(), pipeline); // Actual HTML conversion now? var htmlFile = Path.ChangeExtension(Path.Combine(htmlDir, name), "html"); var featuredImage = string.Empty; _log.Info($"Processing '{htmlFile}'"); foreach (var l in doc.Descendants().OfType <LinkInline>().Where(l => l.IsImage)) { var img = new FileInfo(Path.Combine(mdFile.DirectoryName, l.Url)); if (img.Exists) { if (_config.InputDirectory != _config.OutputDirectory) { img.CopyTo(Path.Combine(htmlDir, l.Url), true); } l.Url = _config.ImagePrefix + name + '/' + l.Url; if (string.IsNullOrEmpty(featuredImage)) { featuredImage = l.Url; } } } using (var writer = new StreamWriter(new FileStream(htmlFile, FileMode.Create))) { var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(doc); writer.Flush(); } var yaml = doc.Descendants().OfType <YamlFrontMatterBlock>().FirstOrDefault(); _index.Add(name, featuredImage, yaml?.Lines.Lines.Select(l => l.ToString())); } }
public static void TestSpec(string baseUrl, string markdown, string expectedLink) { var pipeline = new MarkdownPipelineBuilder().Build(); var writer = new StringWriter(); var renderer = new HtmlRenderer(writer); if (baseUrl != null) { renderer.BaseUrl = new Uri(baseUrl); } pipeline.Setup(renderer); var document = MarkdownParser.Parse(markdown, pipeline); renderer.Render(document); writer.Flush(); Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\"")); }
public void RendersParsedHtml() { string _markdown = $"[file://{Directory.GetCurrentDirectory()}/gist.js]"; var _pipeline = new MarkdownPipelineBuilder() .UseEmbeddedGists(config => { config.AddBaseUrl("file://"); }).Build(); _pipeline.Setup(_renderer); var _html = Markdown.ToHtml(_markdown, _pipeline); var _result = new HtmlDocument(); _result.LoadHtml($"<!DOCTYPE html><html><body>{_html}</body></html>"); _html.Trim().Should().NotContain("document.write"); _result.DocumentNode.SelectNodes("//p/link").Count().Should().Be(1); _result.DocumentNode.SelectSingleNode("//p/link").Attributes["test-attribute"].Value.Should().Be("confirmed"); _result.GetElementbyId("file-test-gist-LC1").InnerText.Should().Be("This is a test gist"); }
public string Render(string markdown, Func <string, string> linkRewriter = null) { var writer = new StringWriter(); var renderer = new HtmlRenderer(writer); renderer.LinkRewriter = linkRewriter; var pipeline = new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UseHighlightJs(new CustomHighlightJsEngine()) .Build(); pipeline.Setup(renderer); var document = Markdown.Parse(markdown, pipeline); renderer.Render(document); writer.Flush(); return(writer.ToString()); }
public static string Converter2(string mixedHtmlAndMarkdown, Action <MarkdownDocument> transform, MarkdownPipeline pipeline) { var converter = new Html2Markdown.Converter(); string markdownOnly = converter.Convert(mixedHtmlAndMarkdown); pipeline = new MarkdownPipelineBuilder() .UseAdvancedExtensions() .UsePipeTables() .Build(); pipeline.Extensions.AddIfNotAlready <SoftlineBreakAsHardlineExtension>(); MarkdownDocument ast = Markdown.Parse(markdownOnly, pipeline); transform(ast); using (var writer = new StringWriter()) { var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(ast); writer.Flush(); return(writer.ToString()); } }
public override void Transform(ExtensionHtmlRenderer extensionHtmlRenderer, WorkflowNotesBlock block, Diagram diagram) { var(element, elementsEnumerable) = _provider.GetBpmnElements(new EaProvider.Path(diagram.Name)); var elements = elementsEnumerable.ToList(); elements.Sort(new BpmnElement.AliasComparer()); var sb = new StringBuilder(); sb.AppendLine($@"# {element.Name}"); var converter = new Html2Markdown.Converter(); foreach (BpmnElement e in elements) { string name = string.IsNullOrEmpty(e.Name) ? e.Alias : e.Name; string notes = converter.Convert(e.Notes); MarkdownDocument notesMd = Markdown.Parse(notes); notesMd.IncreaseHeadingLevel(2); string normalizedNotes = null; using (var writer = new StringWriter()) { var pipeline = new MarkdownPipelineBuilder().Build(); pipeline.Extensions.AddIfNotAlready <SoftlineBreakAsHardlineExtension>(); var renderer = new NormalizeRenderer(writer); pipeline.Setup(renderer); renderer.Render(notesMd); writer.Flush(); normalizedNotes = writer.ToString(); } sb.AppendLine($@"## {name}"); sb.AppendLine($@"Lane: {e.Lane}"); sb.AppendLine(); sb.AppendLine($@"Description:"); sb.AppendLine(normalizedNotes); } MarkdownDocument document = Markdown.Parse(sb.ToString()); Replace(block, document); }
public async Task <string> GetHtml(string localGetImgRelativeURI) { var httpClient = new HttpClient(); string mdBody = await httpClient.GetStringAsync(_rawSourceMDUrl).ConfigureAwait(false); var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); var document = MarkdownParser.Parse(mdBody, pipeline); var htmlRenderer = new HtmlRenderer(new StringWriter()); // Old approach for building local images url. // Removed because it does not manage explicit <img> tag inside MD files // //htmlRenderer.BaseUrl = new Uri(_rawBaseSourceUrl); //if (localGetImgRelativeURI != null) //{ // htmlRenderer.LinkRewriter = (oldlink) => // { // if (oldlink.StartsWith(_rawBaseSourceUrl)) // { // return localGetImgRelativeURI + Convert.ToBase64String(Encoding.UTF8.GetBytes(oldlink.Replace(_rawBaseSourceUrl, ""))); // } // else // { // return oldlink; // } // }; //} pipeline.Setup(htmlRenderer); htmlRenderer.Render(document); htmlRenderer.Writer.Flush(); string htmlBody = htmlRenderer.Writer.ToString(); htmlBody = await SetImgUrls(htmlBody, localGetImgRelativeURI); return(htmlBody); }
public string Transform(string source) { // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/YamlSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/BootstrapSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/EmphasisExtraSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/DefinitionListSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/FootnotesSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/AutoLinks.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/ListExtraSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/MediaSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/AbbreviationSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/HardlineBreakSpecs.md // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md // https://github.com/ilich/Markdig.Prism/blob/main/src/Markdig.Prism/PrismCodeBlockRenderer.cs var pipeline = new MarkdownPipelineBuilder() .UseYamlFrontMatter() // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/EmojiSpecs.md //.UseEmojiAndSmiley(new Markdig.Extensions.Emoji.EmojiMapping(new Dictionary<string, string>() { { ":smiley:", "♥" } }, new Dictionary<string, string>())) // UseAdvancedExtensions 2021-01-25 // .UseAbbreviations() // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/AutoIdentifierSpecs.md .UseAutoIdentifiers() // .UseCitations() // .UseCustomContainers() // .UseDefinitionLists() // .UseEmphasisExtras() // .UseFigures() // .UseFooters() // .UseFootnotes() //.UseGridTables() // .UseMathematics() // .UseMediaLinks() // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/PipeTableSpecs.md .UsePipeTables() // .UseListExtras() // .UseTaskLists() // .UseDiagrams() // .UseAutoLinks() // https://github.com/xoofx/markdig/blob/master/src/Markdig.Tests/Specs/GenericAttributesSpecs.md .UseGenericAttributes() .Build(); var doc = Markdown.Parse(source, pipeline); // Process headings to insert an intermediate LinkInline foreach (var headingBlock in doc.Descendants <HeadingBlock>()) { var inline = new LinkInline($"#{headingBlock.GetAttributes().Id}", null); var previousInline = headingBlock.Inline; headingBlock.Inline = null; inline.AppendChild(previousInline); headingBlock.Inline = inline; } var anchorTags = doc.Descendants <LinkInline>(); foreach (var anchor in anchorTags) { if (anchor is LinkInline link && !link.IsImage) { if (!anchor.Url.StartsWith(GlobalFunctions.Instance.Url)) { link.GetAttributes().AddClass("external"); } } // TODO disable pending Medium response... // if (anchor is LinkInline imageLink && imageLink.IsImage) // { // if (imageLink.Url.StartsWith("/assets")) // { // imageLink.Url = GlobalFunctions.Instance.Url + imageLink.Url; // } // } } // Render the doc var writer = new StringWriter(); var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(doc); return(writer.ToString().Trim()); }
private string ConvertMarkdownToHTML(List <MarkdownFile> files) { Log("Converting Markdown to HTML"); StringBuilder sb = new StringBuilder(); for (var i = 0; i < files.Count; i++) { var mf = files[i]; var file = new FileInfo(files[i].AbsolutePath); Log($"parsing file {file.Name}", LogLevel.Debug); var htmlfile = file.FullName.Replace(".md", ".html"); var md = File.ReadAllText(file.FullName); //setup the markdown pipeline to support tables var pipeline = new MarkdownPipelineBuilder().UsePipeTables().Build(); //parse the markdown document so we can alter it later var document = (MarkdownObject)Markdown.Parse(md, pipeline); //adjust the links CorrectLinksAndImages(document, file, mf); string html = null; var builder = new StringBuilder(); using (var writer = new System.IO.StringWriter(builder)) { // write the HTML output var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(document); } html = builder.ToString(); //add html anchor var anchorPath = file.FullName.Substring(_path.Length); anchorPath = anchorPath.Replace("\\", ""); anchorPath = anchorPath.ToLower(); anchorPath = anchorPath.Replace(".md", ""); var relativePath = file.FullName.Substring(_path.Length); var anchor = $"<a id=\"{anchorPath}\"> </a>"; Log($"\tAnchor: {anchorPath}"); html = anchor + html; if (_options.PathToHeading) { var filename = file.Name; filename = HttpUtility.UrlDecode(relativePath); var heading = $"<b>{filename}</b>"; html = heading + html; } if (_options.Heading) { var filename = file.Name.Replace(".md", ""); filename = HttpUtility.UrlDecode(filename); var heading = $"<h1>{filename}</h1>"; html = heading + html; } if (_options.BreakPage) { //if not one the last page if (i + 1 < files.Count) { Log("Adding new page to PDF"); html = "<div style='page-break-after: always;'>" + html + "</div>"; } } if (_options.Debug) { Log($"html:\n{html}"); } sb.Append(html); } var result = sb.ToString(); return(result); }
/// <summary> /// Constructs a DocComment instance from the documentation comments /// associated with a source code element. /// </summary> /// <param name="docComments">The doc comments from the source code</param> /// <param name="name">The name of the element</param> /// <param name="deprecated">Flag indicating whether or not the element had a Deprecated attribute</param> /// <param name="replacement">The name of the replacement element for deprecated elements, if given</param> public DocComment(IEnumerable <string> docComments, string name, bool deprecated, string replacement) { string GetHeadingText(HeadingBlock heading) { var sb = new StringBuilder(); foreach (var item in heading.Inline) { sb.Append(item.ToString()); } return(sb.ToString()); } string GetParagraphText(LeafBlock leaf) { var sb = new StringBuilder(); foreach (var item in leaf.Inline) { sb.Append(item.ToString()); } return(sb.ToString()); } string ToMarkdown(IEnumerable <Block> blocks) { var writer = new StringWriter(); var renderer = new NormalizeRenderer(writer); var pipeline = new MarkdownPipelineBuilder().Build(); pipeline.Setup(renderer); foreach (var block in blocks) { renderer.Render(block); } // We convert \n to \r because the YAML serialization will eventually // output \n\n for \n, but \r\n for \r. return(writer.ToString().TrimEnd().Replace('\n', '\r')); } List <ValueTuple <string, List <Block> > > BreakIntoSections(IEnumerable <Block> blocks, int level) { var key = ""; var accum = new List <Block>(); var result = new List <ValueTuple <string, List <Block> > >(); foreach (var block in blocks) { if (block is HeadingBlock heading) { if (heading.Level == level) { if (accum.Count > 0) { result.Add(new ValueTuple <string, List <Block> >(key, accum)); accum = new List <Block>(); } key = GetHeadingText(heading); } else { accum.Add(block); } } else { accum.Add(block); } } if (accum.Count > 0) { result.Add(new ValueTuple <string, List <Block> >(key, accum)); } return(result); } void ParseListSection(IEnumerable <Block> blocks, List <string> accum, bool lowerCase) { foreach (var block in blocks) { if (block is ListBlock list) { foreach (var sub in block.Descendants()) { if (sub is ListItemBlock item) { // Some special treatment for funky doc comments in some of the Canon if (item.Count == 1 && item.LastChild is LeafBlock leaf && leaf.Inline != null && leaf.Inline.FirstChild is LiteralInline literal) { var itemText = lowerCase ? GetParagraphText(leaf).ToLowerInvariant() : GetParagraphText(leaf); if (itemText.StartsWith("@\"") && itemText.EndsWith("\"")) { itemText = itemText.Substring(2, itemText.Length - 3); } literal.Content = new Markdig.Helpers.StringSlice(itemText.ToLowerInvariant()); } accum.Add(ToMarkdown(new Block[] { item })); } } } } } void ParseMapSection(IEnumerable <Block> blocks, Dictionary <string, string> accum) { var subsections = BreakIntoSections(blocks, 2); foreach ((var key, var subs) in subsections) { // TODO: when we add the ability to flag warnings from the doc comment builder, // we should check here for duplicate keys and generate a warning if appropriate. accum[key] = ToMarkdown(subs); } } // First element is not matching, second is matching (List <Block>, List <Block>) PartitionNestedSection(IEnumerable <Block> blocks, int level, string name) { var inMatch = false; var result = (new List <Block>(), new List <Block>()); foreach (var block in blocks) { var skip = false; if ((block is HeadingBlock heading) && (heading.Level == level)) { inMatch = GetHeadingText(heading).Equals(name); skip = true; } if (inMatch) { if (!skip) { result.Item2.Add(block); } } else { result.Item1.Add(block); } } return(result); } // Initialize to safe empty values this.Summary = ""; this.Description = ""; this.ShortSummary = ""; this.Documentation = ""; this.Input = new Dictionary <string, string>(); this.Output = ""; this.TypeParameters = new Dictionary <string, string>(); this.Example = ""; this.Remarks = ""; this.SeeAlso = new List <string>(); this.References = ""; var deprecationSummary = String.IsNullOrWhiteSpace(replacement) ? DiagnosticItem.Message(WarningCode.DeprecationWithoutRedirect, new string[] { name }) : DiagnosticItem.Message(WarningCode.DeprecationWithRedirect, new string[] { name, "@\"" + replacement + "\"" }); var deprecationDetails = ""; var text = String.Join("\n", docComments); // Only parse if there are comments to parse if (!string.IsNullOrWhiteSpace(text)) { var doc = Markdown.Parse(text); var sections = BreakIntoSections(doc, 1); List <Block> summarySection = new List <Block>(); List <Block> descriptionSection = new List <Block>(); foreach ((var tag, var section) in sections) { switch (tag) { case "Summary": this.Summary = ToMarkdown(section); summarySection.AddRange(section); // For now, the short hover information gets the first paragraph of the summary. this.ShortSummary = ToMarkdown(section.GetRange(0, 1)); break; case "Deprecated": if (String.IsNullOrWhiteSpace(name)) { deprecationSummary = ToMarkdown(section.GetRange(0, 1)); if (section.Count > 1) { deprecationDetails = ToMarkdown(section.GetRange(1, section.Count - 1)); } } else { deprecationDetails = ToMarkdown(section); } deprecated = true; break; case "Description": this.Description = ToMarkdown(section); descriptionSection = section; break; case "Input": ParseMapSection(section, this.Input); break; case "Output": this.Output = ToMarkdown(section); break; case "Type Parameters": ParseMapSection(section, this.TypeParameters); break; case "Example": this.Example = ToMarkdown(section); break; case "Remarks": (var remarks, var examples) = PartitionNestedSection(section, 2, "Example"); if ((examples.Count > 0) && (this.Example == "")) { this.Example = ToMarkdown(examples); } this.Remarks = ToMarkdown(remarks); break; case "See Also": // seeAlso is a list of UIDs, which are all lower case, // so pass true to lowercase all strings found in this section ParseListSection(section, this.SeeAlso, true); break; case "References": this.References = ToMarkdown(section); break; default: // TODO: add diagnostic warning about unknown tag break; } } this.Documentation = ToMarkdown(summarySection.Concat(descriptionSection)); } if (deprecated) { var shortDeprecationText = DeprecatedWarning + "\r" + deprecationSummary; var longDeprecationText = shortDeprecationText + (String.IsNullOrWhiteSpace(deprecationDetails) ? "" : "\r") + deprecationDetails; this.Summary += "\r" + longDeprecationText; this.ShortSummary = shortDeprecationText; this.Documentation = deprecationSummary; } }
private string ConvertMarkdownToHTML(List <MarkdownFile> files) { Log("Converting Markdown to HTML"); StringBuilder sb = new StringBuilder(); for (var i = 0; i < files.Count; i++) { var mf = files[i]; var file = new FileInfo(files[i].AbsolutePath); Log($"parsing file {file.Name}", LogLevel.Debug); var htmlfile = file.FullName.Replace(".md", ".html"); if (!File.Exists(file.FullName)) { Log($"File {file.FullName} specified in the order file was not found and will be skipped!", LogLevel.Error); continue; } var md = File.ReadAllText(file.FullName); // remove scalings from image links var regexImageScalings = @"\(((.[^\)]*?(png|jpg|jpeg))( =((\d+).*x(\d+).*)))\)"; md = Regex.Replace(md, regexImageScalings, @"($2){width=$6 height=$7}"); //setup the markdown pipeline to support tables var pipeline = new MarkdownPipelineBuilder() .UsePipeTables() .UseEmojiAndSmiley() .UseAdvancedExtensions() .Build(); //parse the markdown document so we can alter it later var document = (MarkdownObject)Markdown.Parse(md, pipeline); //adjust the links CorrectLinksAndImages(document, file, mf); string html = null; var builder = new StringBuilder(); using (var writer = new System.IO.StringWriter(builder)) { // write the HTML output var renderer = new HtmlRenderer(writer); pipeline.Setup(renderer); renderer.Render(document); } html = builder.ToString(); //add html anchor var anchorPath = file.FullName.Substring(_path.Length); anchorPath = anchorPath.Replace("\\", ""); anchorPath = anchorPath.ToLower(); anchorPath = anchorPath.Replace(".md", ""); var relativePath = file.FullName.Substring(_path.Length); var anchor = $"<a id=\"{anchorPath}\"> </a>"; Log($"\tAnchor: {anchorPath}"); html = anchor + html; if (_options.PathToHeading) { var filename = file.Name; filename = HttpUtility.UrlDecode(relativePath); var heading = $"<b>{filename}</b>"; html = heading + html; } if (_options.Heading) { var filename = file.Name.Replace(".md", ""); filename = HttpUtility.UrlDecode(filename); var filenameEscapes = new Dictionary <string, string> { { "%3A", ":" }, { "%3C", "<" }, { "%3E", ">" }, { "%2A", "*" }, { "%3F", "?" }, { "%7C", "|" }, { "%2D", "-" }, { "%22", "\"" }, { "-", " " } }; var title = new StringBuilder(filename); foreach (var filenameEscape in filenameEscapes) { title.Replace(filenameEscape.Key, filenameEscape.Value); } var heading = $"<h1>{title.ToString()}</h1>"; html = heading + html; } if (_options.BreakPage) { //if not one the last page if (i + 1 < files.Count) { Log("Adding new page to PDF"); html = "<div style='page-break-after: always;'>" + html + "</div>"; } } if (_options.Debug) { Log($"html:\n{html}"); } sb.Append(html); } var result = sb.ToString(); return(result); }