Example #1
0
        /// <summary>
        /// Exports the specified <see cref="TextDocument"/> to an external markdown file.
        /// </summary>
        /// <param name="document">The document to export.</param>
        /// <param name="fileName">Full name of the markdown file to export to.</param>
        public void Export(TextDocument document, string fileName)
        {
            var path = Path.GetDirectoryName(fileName);

            var imagePath = GetImagePath(path);

            var sourceDoc = new System.Text.StringBuilder(document.SourceText);

            var list = new List <(string Url, int urlSpanStart, int urlSpanEnd)>(document.ReferencedImageUrls);

            list.Sort((x, y) => Comparer <int> .Default.Compare(y.urlSpanEnd, x.urlSpanEnd)); // Note the inverse order of x and y to sort urlSpanEnd descending

            var imageStreamProvider = new ImageStreamProvider();

            // Export images
            foreach (var(Url, urlSpanStart, urlSpanEnd) in list)
            {
                using (var stream = new System.IO.MemoryStream())
                {
                    var streamResult = imageStreamProvider.GetImageStream(stream, Url, 300, Altaxo.Main.ProjectFolder.GetFolderPart(document.Name), document.Images);

                    if (streamResult.IsValid)
                    {
                        var hashName = MemoryStreamImageProxy.ComputeStreamHash(stream);

                        var imageFileName = hashName + streamResult.Extension;

                        if (!Directory.Exists(imagePath))
                        {
                            Directory.CreateDirectory(imagePath);
                        }

                        // Copy stream to FileSystem
                        using (var fileStream = new System.IO.FileStream(Path.Combine(imagePath, imageFileName), FileMode.Create, FileAccess.Write, FileShare.Read))
                        {
                            stream.Seek(0, SeekOrigin.Begin);
                            stream.CopyTo(fileStream);
                        }

                        // now change the url in the markdown text
                        var newUrl = ImageDirectoryName + "/" + imageFileName;

                        sourceDoc.Remove(urlSpanStart, 1 + urlSpanEnd - urlSpanStart);
                        sourceDoc.Insert(urlSpanStart, newUrl);
                    }
                }
            }

            // now save the markdown document

            using (var markdownStream = new System.IO.StreamWriter(fileName, false, Encoding.UTF8, 4096))
            {
                markdownStream.Write(sourceDoc.ToString());
            }
        }
Example #2
0
        ExportImages(TextDocument document, string basePathName)
        {
            var imagePath = GetImagePath(basePathName);

            var list = new List <(string Url, int urlSpanStart, int urlSpanEnd)>(document.ReferencedImageUrls);

            list.Sort((x, y) => Comparer <int> .Default.Compare(y.urlSpanEnd, x.urlSpanEnd)); // Note the inverse order of x and y to sort urlSpanEnd descending

            var imageStreamProvider = new ImageStreamProvider();

            var oldToNewImageUrl = new Dictionary <string, string>();
            var listOfReferencedImageFileNames = new HashSet <string>();

            // Export images
            foreach (var(Url, urlSpanStart, urlSpanEnd) in list)
            {
                using (var stream = new System.IO.MemoryStream())
                {
                    var streamResult = imageStreamProvider.GetImageStream(stream, Url, 300, Altaxo.Main.ProjectFolder.GetFolderPart(document.Name), document.Images);

                    if (streamResult.IsValid)
                    {
                        var hashName = MemoryStreamImageProxy.ComputeStreamHash(stream);

                        var imageFileName = hashName + streamResult.Extension;

                        if (!Directory.Exists(imagePath))
                        {
                            Directory.CreateDirectory(imagePath);
                        }

                        // Copy stream to FileSystem
                        var fullImageFileName = Path.Combine(imagePath, imageFileName);
                        using (var fileStream = new System.IO.FileStream(fullImageFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
                        {
                            stream.Seek(0, SeekOrigin.Begin);
                            stream.CopyTo(fileStream);
                        }

                        // now change the url in the markdown text
                        var newUrl = ImageFolderName + "/" + imageFileName;

                        oldToNewImageUrl[Url] = newUrl;
                        listOfReferencedImageFileNames.Add(fullImageFileName);
                    }
                }
            }

            return(oldToNewImageUrl, listOfReferencedImageFileNames);
        }
Example #3
0
        /// <summary>
        /// Converts the graph`s Url to reflect the new location of the expanded document.
        /// </summary>
        /// <param name="url">The URL of the graph.</param>
        /// <param name="originalTextDocumentPath">The project folder of the orginal text document which contained this link.</param>
        /// <param name="newTextDocumentPath">The project folder which will contain the expanded (i.e. the target) text document.</param>
        /// <returns>The converted Url. Urls that can not be resolved will be left untouched.</returns>
        /// <exception cref="InvalidProgramException">We expect here a link to a graph, but what we have is: " + url</exception>
        private static string ConvertGraphUrl(string url, string originalTextDocumentPath, string newTextDocumentPath)
        {
            if (url.ToLowerInvariant().StartsWith(ImagePretext.GraphAbsolutePathPretext))
            {
                // the graphs reference is an absolute path, thus we don't need to change the url
                return(url);
            }
            else if (url.ToLowerInvariant().StartsWith(ImagePretext.GraphRelativePathPretext))
            {
                // the graphs reference is a path relative to the text document
                var graph = ImageStreamProvider.FindGraphWithUrl(url, originalTextDocumentPath);
                if (null == graph)
                {
                    // can't resolve the graph

                    /* Commented out because it makes no sense to try to convert Urls that can not be resolved, and the repeated conversion would give nonsense results
                     * var newRelativePath = ProjectFolder.GetRelativePathFromTo(newTextDocumentPath, originalTextDocumentPath);
                     * newRelativePath += url.Substring(ImagePretext.GraphRelativePathPretext.Length);
                     * return ImagePretext.GraphRelativePathPretext + newRelativePath;
                     */

                    return(url);
                }
                else
                {
                    // the graph could be resolved, thus we can calculate the relative path name directly
                    var newRelativePath = ProjectFolder.GetRelativePathFromTo(newTextDocumentPath, graph.Name);
                    newRelativePath += ProjectFolder.GetNamePart(graph.Name);
                    newRelativePath  = newRelativePath.Replace(" ", "%20").Replace("/", "%2F");
                    return(ImagePretext.GraphRelativePathPretext + newRelativePath);
                }
            }
            else
            {
                throw new InvalidProgramException("We expect here a link to a graph, but what we have is: " + url);
            }
        }
Example #4
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);
        }