Ejemplo n.º 1
0
        /// <summary>
        /// Expands the document to include child documents directly. This process is recursively, i.e. if the child documents contain child-child documents,
        /// they are expanded, too.
        /// </summary>
        /// <param name="textDocument">The original text document. This document is not changed during the expansion.</param>
        /// <param name="convertGraphsToImages">If true, links to Altaxo graphs will be converted to images. If false, the links to the graphs were kept, but the path to the graphs is changed appropriately.</param>
        /// <param name="newPath">Folder path of the final document that is the target of the expansion process.</param>
        /// <param name="recursionLevel">The recursion level. Start with 0 here.</param>
        /// <param name="errors">A list that collects error messages.</param>
        /// <returns>A new <see cref="TextDocument"/>. This text document contains the expanded markdown text. In addition, all Altaxo graphs are converted to local images.</returns>
        /// <remarks>Since finding Altaxo graphs embedded in the markdown is depended on the context (location of the TextDocument and location of the graph),
        /// and we somewhat loose this context during the expansion, we convert the graphs to local images before we insert the document into the master document.</remarks>
        private static TextDocument ExpandDocumentToNewDocument(TextDocument textDocument, bool convertGraphsToImages, string newPath, int recursionLevel = 0, List <MarkdownError> errors = null)
        {
            var resultDocument = new TextDocument();

            resultDocument.AddImagesFrom(textDocument);
            resultDocument.Name = textDocument.Name;

            // first parse the document with markdig
            var pipeline = new MarkdownPipelineBuilder();

            pipeline = MarkdownUtilities.UseSupportedExtensions(pipeline);
            var builtPipeline    = pipeline.Build();
            var markdownDocument = Markdig.Markdown.Parse(textDocument.SourceText, builtPipeline);

            var markdownToProcess = new List <MarkdownObject>();

            foreach (var mdo in MarkdownUtilities.EnumerateAllMarkdownObjectsRecursively(markdownDocument))
            {
                if (mdo is LinkInline link)
                {
                    if (link.Url.ToLowerInvariant().StartsWith(ImagePretext.GraphRelativePathPretext))
                    {
                        markdownToProcess.Add(mdo);
                    }
                }
                else if (mdo is CodeBlock blk)
                {
                    var attr = (Markdig.Renderers.Html.HtmlAttributes)mdo.GetData(typeof(Markdig.Renderers.Html.HtmlAttributes));
                    if (attr != null && attr.Properties != null && attr.Properties.Count >= 2 && attr.Properties[0].Key == "Altaxo" && attr.Properties[1].Key == "child")
                    {
                        var childDoc = attr.Properties[1].Value;
                        if (null != childDoc)
                        {
                            markdownToProcess.Add(mdo);
                        }
                    }
                }
            }

            // now we process the list backwards and change the source

            var documentAsStringBuilder = new StringBuilder(textDocument.SourceText);
            var imageStreamProvider     = new ImageStreamProvider();

            markdownToProcess.Reverse(); // we start from the end of the document, in order not to change the positions of unprocessed markdown
            foreach (var mdo in markdownToProcess)
            {
                if (mdo is LinkInline link)
                {
                    if (convertGraphsToImages) // convert links to graphs to images
                    {
                        using (var stream = new System.IO.MemoryStream())
                        {
                            var streamResult = imageStreamProvider.GetImageStream(stream, link.Url, 300, Altaxo.Main.ProjectFolder.GetFolderPart(textDocument.Name), textDocument.Images);
                            if (null == streamResult.ErrorMessage)
                            {
                                stream.Seek(0, System.IO.SeekOrigin.Begin);
                                var proxy = MemoryStreamImageProxy.FromStream(stream, streamResult.Extension);
                                resultDocument.AddImage(proxy);
                                documentAsStringBuilder.Remove(link.UrlSpan.Value.Start, link.UrlSpan.Value.Length);
                                documentAsStringBuilder.Insert(link.UrlSpan.Value.Start, "local:" + proxy.ContentHash);
                            }
                        }
                    }
                    else // keep link to graphs, but change their path
                    {
                        var newUrl = ConvertGraphUrl(link.Url, textDocument.Name, newPath);
                        if (newUrl != link.Url)
                        {
                            documentAsStringBuilder.Remove(link.UrlSpan.Value.Start, link.UrlSpan.Value.Length);
                            documentAsStringBuilder.Insert(link.UrlSpan.Value.Start, newUrl);
                        }
                    }
                }
                else if (mdo is CodeBlock blk)
                {
                    var attr         = (Markdig.Renderers.Html.HtmlAttributes)mdo.GetData(typeof(Markdig.Renderers.Html.HtmlAttributes));
                    var childDocName = attr.Properties[1].Value;
                    // first, we assume a relative name
                    var fullName = Altaxo.Main.ProjectFolder.GetFolderPart(textDocument.Name) + childDocName;
                    var success  = Current.Project.TextDocumentCollection.TryGetValue(fullName, out var childTextDocument);
                    if (!success) // relative name failed, we try it with the unmodified (absolute) name
                    {
                        success = Current.Project.TextDocumentCollection.TryGetValue(childDocName, out childTextDocument);
                    }

                    if (success)
                    {
                        var expandedChild = ExpandDocumentToNewDocument(childTextDocument, convertGraphsToImages, newPath, recursionLevel + 1, errors);
                        // exchange the source text
                        documentAsStringBuilder.Remove(mdo.Span.Start, mdo.Span.Length);
                        documentAsStringBuilder.Insert(mdo.Span.Start, expandedChild.SourceText);
                        // insert images
                        resultDocument.AddImagesFrom(expandedChild);
                    }
                    else if (null != errors) // report an error
                    {
                        var error = new MarkdownError()
                        {
                            AltaxoDocumentName = textDocument.Name,
                            LineNumber         = blk.Line,
                            ColumnNumber       = blk.Column,
                            ErrorMessage       = string.Format("Could not expand child document \"{0}\" because this name could not be resolved!", childDocName)
                        };

                        errors.Add(error);
                    }
                }
            }

            resultDocument.SourceText = documentAsStringBuilder.ToString();

            if (0 == recursionLevel) // if we are about to return the master document, we must restore the list of referenced image Urls
            {
                markdownDocument = Markdig.Markdown.Parse(resultDocument.SourceText, builtPipeline);
                resultDocument.ReferencedImageUrls = MarkdownUtilities.GetReferencedImageUrls(markdownDocument);
            }

            return(resultDocument);
        }
Ejemplo n.º 2
0
        public string Import(string fileName)
        {
            var errors = new System.Text.StringBuilder();

            string markdownDirectory = System.IO.Path.GetDirectoryName(fileName);
            string markdownText      = null;

            using (var stream = new StreamReader(fileName, Encoding.UTF8, true))
            {
                markdownText = stream.ReadToEnd();
            }

            // Parse the markdown

            var pipeline = new MarkdownPipelineBuilder();

            pipeline = UseSupportedExtensions(pipeline);

            var markdownDocument = Markdig.Markdown.Parse(markdownText, pipeline.Build());

            var textDocument = new TextDocument();

            var images = new List <(Markdig.Syntax.Inlines.LinkInline link, MemoryStreamImageProxy proxy)>();

            foreach (var mdo in EnumerateAllMarkdownObjectsRecursively(markdownDocument))
            {
                if (mdo is Markdig.Syntax.Inlines.LinkInline link && link.IsImage)
                {
                    var url = link.Url;

                    if (string.IsNullOrEmpty(url) || !Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
                    {
                        continue;
                    }

                    var uri = new Uri(url, UriKind.RelativeOrAbsolute);

                    string imgFileName = null;

                    if (uri.IsAbsoluteUri)
                    {
                        imgFileName = uri.AbsolutePath;
                    }
                    else
                    {
                        imgFileName = System.IO.Path.Combine(markdownDirectory, uri.OriginalString);
                    }

                    try
                    {
                        var imgProxy = MemoryStreamImageProxy.FromFile(imgFileName);
                        textDocument.AddImage(imgProxy);
                        images.Add((link, imgProxy));
                    }
                    catch (Exception ex)
                    {
                        errors.AppendFormat("File {0} could not be imported, error: {1}\r\n", imgFileName, ex.Message);
                    }
                }
            }

            // now we have to replace all urls
            var text = new System.Text.StringBuilder(markdownText);

            for (int i = images.Count - 1; i >= 0; --i)
            {
                var(link, proxy) = images[i];

                text.Remove(link.UrlSpan.Value.Start, link.UrlSpan.Value.End + 1 - link.UrlSpan.Value.Start);
                text.Insert(link.UrlSpan.Value.Start, ImagePretext.LocalImagePretext + proxy.ContentHash);
            }

            textDocument.SourceText = text.ToString();

            Current.Project.TextDocumentCollection.Add(textDocument);

            Current.ProjectService.ShowDocumentView(textDocument);

            return(errors.Length == 0 ? null : errors.ToString());
        }