Example #1
0
        private List <string> ConvertFile(string fileContent)
        {
            //get full html string from file
            var fileHTML = string.Empty;

            using (var writer = new StringWriter())
            {
                CommonMarkConverter.ProcessStage3(CommonMarkConverter.Parse(fileContent), writer);
                fileHTML += writer.ToString();
            }

            //split the html doc based on headers
            HtmlDocument agilityDoc = new HtmlDocument();

            agilityDoc.LoadHtml(fileHTML);
            var           nodes    = agilityDoc.DocumentNode.ChildNodes.ToArray();
            List <string> sections = nodes.Skip(1).Aggregate(nodes.Take(1).Select(x => x.OuterHtml).ToList(), (a, n) =>
            {
                if (n.Name.ToLower() == "h1" || n.Name.ToLower() == "h2")
                {
                    a.Add("");
                }
                a[a.Count - 1] += n.OuterHtml; return(a);
            });

            return(sections);
        }
        public override RadDocument Import(Stream input)
        {
            using (StreamReader reader = new StreamReader(input))
            {
                // Performs the first stage of the conversion - parses block elements from the source
                // and created the syntax tree.
                var blockDoc = CommonMarkConverter.ProcessStage1(reader);

                //Performs the second stage of the conversion - parses block element contents into
                //inline elements.
                CommonMarkConverter.ProcessStage2(blockDoc);

                string       tempFileName = System.IO.Path.GetTempFileName();
                StreamWriter writer       = new StreamWriter(tempFileName);
                using (writer)
                {
                    // Performs the last stage of the conversion - converts the syntax tree to HTML
                    // representation.
                    CommonMarkConverter.ProcessStage3(blockDoc, writer);
                }

                RadDocument document;
                using (FileStream stream = File.OpenRead(tempFileName))
                {
                    document = htmlProvider.Import(stream);
                }
                File.Delete(tempFileName);

                return(document);
            }
        }
Example #3
0
        private static string GetMarkupFromMarkdown(string examId, string text)
        {
            var document = CommonMarkConverter.Parse(text);

            // walk the document node tree
            foreach (var node in document.AsEnumerable())
            {
                if (
                    // start and end of each node may be visited separately
                    node.IsOpening
                    // blocks are elemets like paragraphs and lists, inlines are
                    // elements like emphasis, links, images.
                    && node.Inline != null &&
                    node.Inline.Tag == InlineTag.Image)
                {
                    node.Inline.TargetUrl = $"/api/Exams/{examId}/Image?path={WebUtility.UrlEncode(Path.GetFileName(node.Inline.TargetUrl))}";
                }
            }

            using (var writer = new System.IO.StringWriter())
            {
                // write the HTML output
                CommonMarkConverter.ProcessStage3(document, writer);
                return(writer.ToString());
            }
        }
Example #4
0
        public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null)
        {
            if (settings == null)
            {
                settings = CommonMarkSettings.Default.Clone();
            }

            Helpers.LogValue("CommonMark", Denormalize(commonMark));
            Helpers.LogValue("Expected", Denormalize(html));

            // Arrange
            commonMark = Helpers.Normalize(commonMark);
            html       = Helpers.Normalize(html);

            string actual;

            Syntax.Block document;

            // Act
            using (var reader = new System.IO.StringReader(commonMark))
                using (var writer = new System.IO.StringWriter())
                {
                    document = CommonMarkConverter.ProcessStage1(reader, settings);
                    CommonMarkConverter.ProcessStage2(document, settings);
                    CommonMarkConverter.ProcessStage3(document, writer, settings);
                    actual = writer.ToString();
                }

            // Assert
            Helpers.LogValue("Actual", Denormalize(actual));
            actual = Helpers.Tidy(actual);
            Assert.AreEqual(Helpers.Tidy(html), actual);

            // Verify that the extendable HTML formatter returns the same result
            var settingsHtmlFormatter = settings.Clone();

            settingsHtmlFormatter.OutputDelegate = (doc, target, stngs) => new Formatters.HtmlFormatter(target, stngs).WriteDocument(doc);
            var actual2 = CommonMarkConverter.Convert(commonMark, settingsHtmlFormatter);

            Assert.AreEqual(actual, Helpers.Tidy(actual2), "HtmlFormatter returned a different result than HtmlFormatterSlim.");

            // Additionally verify that the parser included source position information.
            // This is done here to catch cases during specification tests that might not be
            // covered in SourcePositionTests.cs.
            var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0);

            if (firstFail != null)
            {
                Assert.Fail("Incorrect source position: " + firstFail);
            }
        }
            public TeePass(string markdown)
            {
                var stopwatch = new Stopwatch();

                stopwatch.Start();
                Block     = CommonMarkConverter.Parse(markdown);
                ParseTime = stopwatch.Elapsed;

                using (var writer = new StringWriter()) {
                    stopwatch.Restart();
                    CommonMarkConverter.ProcessStage3(Block, writer);
                    FormatTime = stopwatch.Elapsed;
                    Html       = writer.ToString();
                }
            }
        private static void DumpTree(string markdown)
        {
            using (var stringReader = new StringReader(markdown))
            {
                var document = CommonMarkConverter.ProcessStage1(stringReader);
                CommonMarkConverter.ProcessStage2(document);

                CommonMarkConverter.ProcessStage3(document, Console.Out, new CommonMarkSettings
                {
                    OutputFormat = OutputFormat.SyntaxTree
                });
            }

            Console.WriteLine();
            Console.WriteLine();
        }
Example #7
0
        public static MarkdownPage Load(string id, MarkdownBundle bundle, TextReader reader, CommonMarkSettings commonMarkSettings)
        {
            string line;
            string title = "";

            while ((line = reader.ReadLine()).StartsWith("@"))
            {
                var match = DirectiveSplitter.Match(line);
                if (!match.Success)
                {
                    throw new Exception($"Invalid directive: {line}");
                }
                string key   = match.Groups[1].Value;
                string value = match.Groups[2].Value;

                if (key == "Title")
                {
                    title = value;
                }
            }
            if (line.Length > 0)
            {
                throw new Exception("Blank line required after directives");
            }

            // TODO: Just use CommonMarkConverter.Convert? (We don't do anything between
            // stages.)
            Block block = CommonMarkConverter.ProcessStage1(reader, commonMarkSettings);

            CommonMarkConverter.ProcessStage2(block, commonMarkSettings);
            var writer = new StringWriter();

            CommonMarkConverter.ProcessStage3(block, writer, commonMarkSettings);

            var content = new HtmlString(writer.ToString());

            return(new MarkdownPage(id, bundle, title, content));
        }
Example #8
0
        public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null)
        {
            Helpers.LogValue("CommonMark", Denormalize(commonMark));
            Helpers.LogValue("Expected", Denormalize(html));

            // Arrange
            commonMark = Helpers.Normalize(commonMark);
            html       = Helpers.Normalize(html);

            string actual;

            Syntax.Block document;

            // Act
            using (var reader = new System.IO.StringReader(commonMark))
                using (var writer = new System.IO.StringWriter())
                {
                    document = CommonMarkConverter.ProcessStage1(reader, settings);
                    CommonMarkConverter.ProcessStage2(document, settings);
                    CommonMarkConverter.ProcessStage3(document, writer, settings);
                    actual = writer.ToString();
                }

            // Assert
            Helpers.LogValue("Actual", Denormalize(actual));
            Assert.AreEqual(Helpers.Tidy(html), Helpers.Tidy(actual));

            // Additionally verify that the parser included source position information.
            // This is done here to catch cases during specification tests that might not be
            // covered in SourcePositionTests.cs.
            var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0);

            if (firstFail != null)
            {
                Assert.Fail("Incorrect source position: " + firstFail);
            }
        }
Example #9
0
        /// <summary>
        /// Generates documentation.
        /// </summary>
        /// <returns>The generation errors.</returns>
        public Model.GenerationError[] Generate(Configuration configuration)
        {
            // Initialize the list containing all generation errors
            List <Model.GenerationError> generationError = new List <Model.GenerationError>();

            // Ensure output directory exists
            if (!this.OutputDirectory.Exists)
            {
                this.OutputDirectory.Create();
            }

            // Process all pages
            List <Model.Page> pages = new List <Model.Page>();

            foreach (Page page in configuration.Pages)
            {
                // Compute the page id used as a tab id and page prefix for bookmarking
                string pageId = page.Path.Replace(".", string.Empty).Replace("/", string.Empty).Replace("\\", string.Empty);

                // Load the document
                Block document;

                // Process the page
                string pageFilePath = this.fileSystem.FileInfo.FromFileName(page.FileSystemPath).FullName;
                using (StreamReader reader = new StreamReader(this.fileSystem.File.Open(pageFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)))
                {
                    document = CommonMarkConverter.ProcessStage1(reader);
                }

                // Process snippet
                CommonMarkConverter.ProcessStage2(document);
                Dictionary <Guid, Extension.Model.Snippet> snippetDictionary = new Dictionary <Guid, Extension.Model.Snippet>();
                foreach (var node in document.AsEnumerable())
                {
                    // Filter fenced code
                    if (node.Block != null && node.Block.Tag == BlockTag.FencedCode)
                    {
                        // Build extraction rule
                        string fencedCode = node.Block.FencedCodeData.Info;

                        // Do not trigger extraction if the fenced code is empty
                        if (string.IsNullOrWhiteSpace(fencedCode))
                        {
                            continue;
                        }

                        SnippetExtractionRule snippetExtractionRule = SnippetExtractionRule.Parse(fencedCode);

                        // Extract and inject snippet and the factory were able to create an extractor
                        if (null != snippetExtractionRule)
                        {
                            // Cleanup Projbook specific syntax
                            node.Block.FencedCodeData.Info = snippetExtractionRule.Language;

                            // Inject snippet
                            try
                            {
                                // Retrieve the extractor instance
                                ISnippetExtractor snippetExtractor;
                                if (!this.extractorCache.TryGetValue(snippetExtractionRule.TargetPath, out snippetExtractor))
                                {
                                    snippetExtractor = this.snippetExtractorFactory.CreateExtractor(snippetExtractionRule);
                                    this.extractorCache[snippetExtractionRule.TargetPath] = snippetExtractor;
                                }

                                // Look for the file in available source directories
                                FileSystemInfoBase  fileSystemInfo = null;
                                DirectoryInfoBase[] directoryInfos = null;
                                if (TargetType.FreeText != snippetExtractor.TargetType)
                                {
                                    directoryInfos = this.ExtractSourceDirectories(this.CsprojFile);
                                    foreach (DirectoryInfoBase directoryInfo in directoryInfos)
                                    {
                                        // Get directory name
                                        string directoryName = directoryInfo.FullName;
                                        if (1 == directoryName.Length && Path.DirectorySeparatorChar == directoryName[0])
                                        {
                                            directoryName = string.Empty;
                                        }

                                        // Compute file full path
                                        string fullFilePath = this.fileSystem.Path.GetFullPath(this.fileSystem.Path.Combine(directoryName, snippetExtractionRule.TargetPath ?? string.Empty));
                                        switch (snippetExtractor.TargetType)
                                        {
                                        case TargetType.File:
                                            if (this.fileSystem.File.Exists(fullFilePath))
                                            {
                                                fileSystemInfo = this.fileSystem.FileInfo.FromFileName(fullFilePath);
                                            }
                                            break;

                                        case TargetType.Folder:
                                            if (this.fileSystem.Directory.Exists(fullFilePath))
                                            {
                                                fileSystemInfo = this.fileSystem.DirectoryInfo.FromDirectoryName(fullFilePath);
                                            }
                                            break;
                                        }

                                        // Stop lookup if the file system info is found
                                        if (null != fileSystemInfo)
                                        {
                                            break;
                                        }
                                    }
                                }

                                // Raise an error if cannot find the file
                                if (null == fileSystemInfo && TargetType.FreeText != snippetExtractor.TargetType)
                                {
                                    // Locate block line
                                    int line = this.LocateBlockLine(node.Block, page);

                                    // Compute error column: Index of the path in the fenced code + 3 (for the ``` prefix) + 1 (to be 1 based)
                                    int column = fencedCode.IndexOf(snippetExtractionRule.TargetPath) + 4;

                                    // Report error
                                    generationError.Add(new Model.GenerationError(
                                                            sourceFile: page.Path,
                                                            message: string.Format("Cannot find target '{0}' in any referenced project ({0})", snippetExtractionRule.TargetPath, string.Join(";", directoryInfos.Select(x => x.FullName))),
                                                            line: line,
                                                            column: column));
                                    continue;
                                }

                                // Extract the snippet
                                Extension.Model.Snippet snippet =
                                    TargetType.FreeText == snippetExtractor.TargetType
                                    ? snippetExtractor.Extract(null, snippetExtractionRule.TargetPath)
                                    : snippetExtractor.Extract(fileSystemInfo, snippetExtractionRule.Pattern);

                                // Reference snippet
                                Guid guid = Guid.NewGuid();
                                snippetDictionary[guid] = snippet;

                                // Inject reference as content
                                StringContent code    = new StringContent();
                                string        content = SNIPPET_REFERENCE_PREFIX + guid;
                                code.Append(content, 0, content.Length);
                                node.Block.StringContent = code;

                                // Change tag to html for node snippets
                                NodeSnippet nodeSnippet = snippet as NodeSnippet;
                                if (null != nodeSnippet)
                                {
                                    node.Block.Tag = BlockTag.HtmlBlock;
                                }
                            }
                            catch (SnippetExtractionException snippetExtraction)
                            {
                                // Locate block line
                                int line = this.LocateBlockLine(node.Block, page);

                                // Compute error column: Fenced code length - pattern length + 3 (for the ``` prefix) + 1 (to be 1 based)
                                int column = fencedCode.Length - snippetExtractionRule.Pattern.Length + 4;

                                // Report error
                                generationError.Add(new Model.GenerationError(
                                                        sourceFile: page.Path,
                                                        message: string.Format("{0}: {1}", snippetExtraction.Message, snippetExtraction.Pattern),
                                                        line: line,
                                                        column: column));
                            }
                            catch (System.Exception exception)
                            {
                                generationError.Add(new Model.GenerationError(
                                                        sourceFile: page.Path,
                                                        message: exception.Message,
                                                        line: 0,
                                                        column: 0));
                            }
                        }
                    }
                }

                // Write to output
                ProjbookHtmlFormatter projbookHtmlFormatter = null;
                MemoryStream          documentStream        = new MemoryStream();
                using (StreamWriter writer = new StreamWriter(documentStream))
                {
                    // Setup custom formatter
                    CommonMarkSettings.Default.OutputDelegate = (d, o, s) => (projbookHtmlFormatter = new ProjbookHtmlFormatter(pageId, o, s, configuration.SectionTitleBase, snippetDictionary, SNIPPET_REFERENCE_PREFIX)).WriteDocument(d);

                    // Render
                    CommonMarkConverter.ProcessStage3(document, writer);
                }

                // Initialize the pre section content
                string preSectionContent = string.Empty;

                // Retrieve page content
                byte[] pageContent = documentStream.ToArray();

                // Set the whole page content if no page break is detected
                if (projbookHtmlFormatter.PageBreak.Length == 0)
                {
                    preSectionContent = System.Text.Encoding.UTF8.GetString(pageContent);
                }

                // Compute pre section content from the position 0 to the first page break position
                if (projbookHtmlFormatter.PageBreak.Length > 0 && projbookHtmlFormatter.PageBreak.First().Position > 0)
                {
                    preSectionContent = this.StringFromByteArray(pageContent, 0, projbookHtmlFormatter.PageBreak.First().Position);
                }

                // Build section list
                List <Model.Section> sections = new List <Model.Section>();
                for (int i = 0; i < projbookHtmlFormatter.PageBreak.Length; ++i)
                {
                    // Retrieve the current page break
                    PageBreakInfo pageBreak = projbookHtmlFormatter.PageBreak[i];

                    // Extract the content from the current page break to the next one if any
                    string content = null;
                    if (i < projbookHtmlFormatter.PageBreak.Length - 1)
                    {
                        PageBreakInfo nextBreak = projbookHtmlFormatter.PageBreak[1 + i];
                        content = this.StringFromByteArray(pageContent, pageBreak.Position, nextBreak.Position - pageBreak.Position);
                    }

                    // Otherwise extract the content from the current page break to the end of the content
                    else
                    {
                        content = this.StringFromByteArray(pageContent, pageBreak.Position, pageContent.Length - pageBreak.Position);
                    }

                    // Create a new section and add to the known list
                    sections.Add(new Model.Section(
                                     id: pageBreak.Id,
                                     level: pageBreak.Level,
                                     title: pageBreak.Title,
                                     content: content));
                }

                // Add new page
                pages.Add(new Model.Page(
                              id: pageId,
                              title: page.Title,
                              preSectionContent: preSectionContent,
                              sections: sections.ToArray()));
            }

            // Html generation
            if (configuration.GenerateHtml)
            {
                try
                {
                    string outputFileHtml = this.fileSystem.Path.Combine(this.OutputDirectory.FullName, configuration.OutputHtml);
                    this.GenerateFile(configuration.TemplateHtml, outputFileHtml, configuration, pages);
                }
                catch (TemplateParsingException templateParsingException)
                {
                    generationError.Add(new Model.GenerationError(configuration.TemplateHtml, string.Format("Error during HTML generation: {0}", templateParsingException.Message), templateParsingException.Line, templateParsingException.Column));
                }
                catch (System.Exception exception)
                {
                    generationError.Add(new Model.GenerationError(configuration.TemplateHtml, string.Format("Error during HTML generation: {0}", exception.Message), 0, 0));
                }
            }

            // Pdf generation
            if (configuration.GeneratePdf && !this.SkipPdf)
            {
                try
                {
                    // Generate the pdf template
                    string outputFileHtml = this.fileSystem.Path.Combine(this.OutputDirectory.FullName, configuration.OutputPdf);
                    this.GenerateFile(configuration.TemplatePdf, outputFileHtml, configuration, pages);

#if !NOPDF
                    // Compute file names
                    string outputPdf     = this.fileSystem.Path.ChangeExtension(configuration.OutputPdf, ".pdf");
                    string outputFilePdf = this.fileSystem.Path.Combine(this.OutputDirectory.FullName, outputPdf);

                    // Prepare the converter
                    MultiplexingConverter pdfConverter = new MultiplexingConverter();
                    pdfConverter.ObjectSettings.Page = outputFileHtml;
                    pdfConverter.Error += (s, e) => {
                        generationError.Add(new Model.GenerationError(configuration.TemplatePdf, string.Format("Error during PDF generation: {0}", e.Value), 0, 0));
                    };

                    // Prepare file system if abstracted
                    bool requireCopyToFileSystem = !File.Exists(outputFileHtml);
                    try
                    {
                        // File system may be abstracted, this requires to copy the pdf generation file to the actual file system
                        // in order to allow wkhtmltopdf to process the generated html as input file
                        if (requireCopyToFileSystem)
                        {
                            File.WriteAllBytes(outputFileHtml, this.fileSystem.File.ReadAllBytes(outputFileHtml));
                        }

                        // Run pdf converter
                        using (pdfConverter)
                            using (Stream outputFileStream = this.fileSystem.File.Open(outputFilePdf, FileMode.Create, FileAccess.Write, FileShare.None))
                            {
                                try
                                {
                                    byte[] buffer = pdfConverter.Convert();
                                    outputFileStream.Write(buffer, 0, buffer.Length);
                                }
                                catch
                                {
                                    // Ignore generation errors at that level
                                    // Errors are handled by the error handling having the best description
                                }
                            }
                    }
                    finally
                    {
                        if (requireCopyToFileSystem && File.Exists(outputFileHtml))
                        {
                            File.Delete(outputFileHtml);
                        }
                    }
#endif
                }
                catch (TemplateParsingException templateParsingException)
                {
                    generationError.Add(new Model.GenerationError(configuration.TemplatePdf, string.Format("Error during PDF generation: {0}", templateParsingException.Message), templateParsingException.Line, templateParsingException.Column));
                }
                catch (System.Exception exception)
                {
                    if (null != exception.InnerException && INCORRECT_FORMAT_HRESULT == exception.InnerException.HResult)
                    {
                        // Report detailed error message for wrong architecture loading
                        string runningArchitectureProccess      = IntPtr.Size == 8 ? "x64" : "x86";
                        string otherRunningArchitectureProccess = IntPtr.Size != 8 ? "x64" : "x86";
                        generationError.Add(new Model.GenerationError(configuration.TemplatePdf, string.Format("Error during PDF generation: Could not load wkhtmltopdf for {0}. Try again running as a {1} process.", runningArchitectureProccess, otherRunningArchitectureProccess), 0, 0));
                    }
                    else
                    {
                        // Report unknown error
                        generationError.Add(new Model.GenerationError(configuration.TemplatePdf, string.Format("Error during PDF generation: {0}", exception.Message), 0, 0));
                    }
                }
            }

            // Return the generation errors
            return(generationError.ToArray());
        }
Example #10
0
        /// <summary>
        /// Get converted HTML for readme.md string content.
        /// </summary>
        /// <param name="readMeMd">ReadMe.md content.</param>
        /// <returns>HTML content.</returns>
        internal static string GetReadMeHtml(string readMeMd)
        {
            // HTML encode markdown, except for block quotes, to block inline html.
            var encodedMarkdown = EncodedBlockQuotePattern.Replace(HttpUtility.HtmlEncode(readMeMd), "> ");

            var settings = CommonMarkSettings.Default.Clone();

            settings.RenderSoftLineBreaksAsLineBreaks = true;

            // Parse executes CommonMarkConverter's ProcessStage1 and ProcessStage2.
            var document = CommonMarkConverter.Parse(encodedMarkdown, settings);

            foreach (var node in document.AsEnumerable())
            {
                if (node.IsOpening)
                {
                    var block = node.Block;
                    if (block != null)
                    {
                        switch (block.Tag)
                        {
                        // Demote heading tags so they don't overpower expander headings.
                        case BlockTag.AtxHeading:
                        case BlockTag.SetextHeading:
                            var level = (byte)Math.Min(block.Heading.Level + 1, 6);
                            block.Heading = new HeadingData(level);
                            break;

                        // Decode preformatted blocks to prevent double encoding.
                        // Skip BlockTag.BlockQuote, which are partially decoded upfront.
                        case BlockTag.FencedCode:
                        case BlockTag.IndentedCode:
                            if (block.StringContent != null)
                            {
                                var content          = block.StringContent.TakeFromStart(block.StringContent.Length);
                                var unencodedContent = HttpUtility.HtmlDecode(content);
                                block.StringContent.Replace(unencodedContent, 0, unencodedContent.Length);
                            }
                            break;
                        }
                    }

                    var inline = node.Inline;
                    if (inline != null && inline.Tag == InlineTag.Link)
                    {
                        // Allow only http or https links in markdown.
                        Uri targetUri;
                        if (!(Uri.TryCreate(inline.TargetUrl, UriKind.Absolute, out targetUri) &&
                              (targetUri.Scheme == Uri.UriSchemeHttp || targetUri.Scheme == Uri.UriSchemeHttps)))
                        {
                            inline.TargetUrl = string.Empty;
                        }
                    }
                }
            }

            // CommonMark.Net does not support link attributes, so manually inject nofollow.
            using (var htmlWriter = new StringWriter())
            {
                CommonMarkConverter.ProcessStage3(document, htmlWriter, settings);

                return(CommonMarkLinkPattern.Replace(htmlWriter.ToString(), "$0" + " rel=\"nofollow\"").Trim());
            }
        }