/// <summary>
        /// Process markdown and generate HTML output
        /// </summary>
        /// <param name="context"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);

            string content = null;

            if (Markdown != null)
            {
                content = Markdown.Model?.ToString();
            }

            if (content == null)
            {
                content = (await output.GetChildContentAsync(NullHtmlEncoder.Default))
                          .GetContent(NullHtmlEncoder.Default);
            }

            if (string.IsNullOrEmpty(content))
            {
                return;
            }

            content = content.Trim('\n', '\r');

            string markdown = NormalizeWhiteSpaceText(content);

            var parser = MarkdownParserFactory.GetParser();
            var html   = parser.Parse(markdown);

            output.TagName = null;  // Remove the <markdown> element
            output.Content.SetHtmlContent(html);
        }
        /// <summary>
        /// Parses content from a file on disk from Markdown to HTML.
        /// </summary>
        /// <param name="filename">A physical or virtual filename path. If running under System.Web this method uses MapPath to resolve paths.
        /// For non-HttpContext environments this file name needs to be fully qualified.</param>
        /// <param name="usePragmaLines">Generates line numbers as ids into headers and paragraphs. Useful for previewers to match line numbers to rendered output</param>
        /// <param name="forceReload">Forces the parser to reloaded. Otherwise cached instance is used</param>
        /// <param name="sanitizeHtml">Strips out scriptable tags and attributes for prevent XSS attacks. Minimal implementation.</param>
        /// <returns>HTML result as a string</returns>
        public static string ParseFromFile(string markdownFile, bool usePragmaLines = false, bool forceReload = false,
                                           bool sanitizeHtml = false)
        {
            if (string.IsNullOrEmpty(markdownFile))
            {
                return(markdownFile);
            }

            var context  = MarkdownMiddlewareExtensions.GetHttpContext();
            var filename = HttpRequestExtensions.MapPath(context.Request, markdownFile);

            string markdown = null;

            try
            {
                using (var reader = File.OpenText(filename))
                {
                    markdown = reader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                throw new FileLoadException("Couldn't load Markdown file: " + Path.GetFileName(markdownFile), ex);
            }

            var parser = MarkdownParserFactory.GetParser();
            var html   = parser.Parse(markdown, sanitizeHtml);

            return(html);
        }
        /// <summary>
        /// Process markdown and generate HTML output
        /// </summary>
        /// <param name="context"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);

            string content = null;

            if (!string.IsNullOrEmpty(Filename))
            {
                try
                {
                    var filename = HttpRequestExtensions.MapPath(_httpContext.HttpContext.Request, Filename);

                    using (var reader = File.OpenText(filename))
                    {
                        content = await reader.ReadToEndAsync();
                    }
                }
                catch (Exception ex)
                {
                    throw new FileLoadException("Couldn't load Markdown file: " + Path.GetFileName(Filename), ex);
                }

                if (string.IsNullOrEmpty(content))
                {
                    return;
                }
            }
            else
            {
                if (Markdown != null)
                {
                    content = Markdown.Model?.ToString();
                }

                if (content == null)
                {
                    content = (await output.GetChildContentAsync(NullHtmlEncoder.Default))
                              .GetContent(NullHtmlEncoder.Default);
                }

                if (string.IsNullOrEmpty(content))
                {
                    return;
                }

                content = content.Trim('\n', '\r');
            }

            string markdown = NormalizeWhiteSpaceText(content);

            var parser = MarkdownParserFactory.GetParser();
            var html   = parser.Parse(markdown, SanitizeHtml);

            output.TagName = null;  // Remove the <markdown> element
            output.Content.SetHtmlContent(html);
        }
        /// <summary>
        /// Renders raw markdown from string to HTML
        /// </summary>
        /// <param name="markdown"></param>
        /// <param name="usePragmaLines"></param>
        /// <param name="forceReload"></param>
        /// <returns></returns>
        public static string Parse(string markdown, bool usePragmaLines = false, bool forceReload = false)
        {
            if (string.IsNullOrEmpty(markdown))
            {
                return("");
            }

            var parser = MarkdownParserFactory.GetParser(usePragmaLines, forceReload);

            return(parser.Parse(markdown));
        }