using System; using System.IO; using Svg; using System.Drawing; using System.Drawing.Imaging; using System.Net; using System.Threading; using TheArtOfDev.HtmlRenderer.Core.Entities; using static com.insanitydesign.MarkdownViewerPlusPlus.MarkdownViewer; /// /// /// namespace com.insanitydesign.MarkdownViewerPlusPlus.Forms { /// /// /// public class MarkdownViewerRenderer : AbstractRenderer { /// /// /// public MarkdownViewerHtmlPanel markdownViewerHtmlPanel; /// /// /// /// public MarkdownViewerRenderer(MarkdownViewer markdownViewer) : base(markdownViewer) { } /// /// /// protected override void Init() { base.Init(); // this.markdownViewerHtmlPanel = new MarkdownViewerHtmlPanel(); //Add a custom image loader this.markdownViewerHtmlPanel.ImageLoad += OnImageLoad; //Add to view this.Controls.Add(this.markdownViewerHtmlPanel); this.Controls.SetChildIndex(this.markdownViewerHtmlPanel, 0); } /// /// /// /// /// public override void Render(string text, FileInformation fileInfo) { base.Render(text, fileInfo); this.markdownViewerHtmlPanel.Text = BuildHtml(ConvertedText, fileInfo.FileName); } /// /// Scroll the rendered panel vertically based on the given ration /// taken from Notepad++ /// /// public override void ScrollByRatioVertically(double scrollRatio) { this.markdownViewerHtmlPanel.ScrollByRatioVertically(scrollRatio); } /// /// Custom renderer for SVG images in the markdown as not supported natively. /// @see https://htmlrenderer.codeplex.com/wikipage?title=Rendering%20SVG%20images /// /// /// protected void OnImageLoad(object sender, HtmlImageLoadEventArgs imageLoadEvent) { try { //Get some file information string src = imageLoadEvent.Src; Uri uri = new Uri(src); string extension = Path.GetExtension(src); //Check if local file or web resource switch (uri.Scheme.ToLowerInvariant()) { case "file": //In case of a local file -> Try to load it directly imageLoadEvent.Handled = true; //Tell the event it was handled, so no error border is drawn ThreadPool.QueueUserWorkItem(state => LoadImageFromFile(src, imageLoadEvent)); break; case "http": case "https": //For web resources check extension and parameter, to fetch from e.g. "badge" creating sources if ((extension != null && extension.Equals(".svg", StringComparison.OrdinalIgnoreCase)) || uri.ToString().Contains("svg=")) { //In case of a web resource file -> Load async using (WebClient webClient = new WebClient()) { imageLoadEvent.Handled = true; //Tell the event it was handled, so no error border is drawn webClient.DownloadDataCompleted += (downloadSender, downloadEvent) => { OnDownloadDataCompleted(downloadEvent, imageLoadEvent); }; webClient.DownloadDataAsync(uri); } } break; } } catch { } } /// /// /// /// /// protected void LoadImageFromFile(string src, HtmlImageLoadEventArgs imageLoadEvent) { try { Uri uri = new Uri(src); //Try to load the file as Image from file //Remove the scheme first string srcWithoutScheme = src; int i = srcWithoutScheme.IndexOf(':'); if (i > 0) srcWithoutScheme = srcWithoutScheme.Substring(i + 1).TrimStart('/'); //If not absolute, add the current file path if (!Path.IsPathRooted(srcWithoutScheme)) { uri = new Uri(@"file:///" + this.FileInfo.FileDirectory + "/" + srcWithoutScheme); } //For SVG images: Convert to Bitmap string extension = Path.GetExtension(src); if (extension != null && extension.Equals(".svg", StringComparison.OrdinalIgnoreCase)) { ConvertSvgToBitmap(SvgDocument.Open(uri.LocalPath), imageLoadEvent); } else { //Load uri, 8, 1 imageLoadEvent.Callback((Bitmap)Image.FromFile(uri.LocalPath, true)); } } catch { } //Not able to handle, refer back to orginal process } /// /// /// /// /// protected Bitmap ConvertSvgToBitmap(SvgDocument svgDocument, HtmlImageLoadEventArgs imageLoadEvent) { Bitmap svgImage = new Bitmap((int)svgDocument.Width, (int)svgDocument.Height, PixelFormat.Format32bppArgb); svgDocument.Draw(svgImage); imageLoadEvent.Callback(svgImage); imageLoadEvent.Handled = true; return svgImage; } /// /// /// /// /// protected void OnDownloadDataCompleted(DownloadDataCompletedEventArgs downloadEvent, HtmlImageLoadEventArgs imageLoadEvent) { using (MemoryStream stream = new MemoryStream(downloadEvent.Result)) { ConvertSvgToBitmap(SvgDocument.Open(stream), imageLoadEvent); } } /// /// Release the custom loader /// protected override void Dispose(bool disposing) { if (this.markdownViewerHtmlPanel != null) { this.markdownViewerHtmlPanel.ImageLoad -= OnImageLoad; } base.Dispose(disposing); } } }