1- using com . insanitydesign . MarkdownViewerPlusPlus . Properties ;
2- using PdfSharp . Pdf ;
3- using System ;
1+ using System ;
42using System . IO ;
5- using System . Windows . Forms ;
6- using System . Xml . Linq ;
7- using TheArtOfDev . HtmlRenderer . PdfSharp ;
3+ using Svg ;
4+ using System . Drawing ;
5+ using System . Drawing . Imaging ;
6+ using System . Net ;
7+ using System . Threading ;
8+ using TheArtOfDev . HtmlRenderer . Core . Entities ;
9+ using static com . insanitydesign . MarkdownViewerPlusPlus . MarkdownViewer ;
810
911/// <summary>
1012///
@@ -37,6 +39,8 @@ protected override void Init()
3739 base . Init ( ) ;
3840 //
3941 this . markdownViewerHtmlPanel = new MarkdownViewerHtmlPanel ( ) ;
42+ //Add a custom image loader
43+ this . markdownViewerHtmlPanel . ImageLoad += OnImageLoad ;
4044 //Add to view
4145 this . Controls . Add ( this . markdownViewerHtmlPanel ) ;
4246 }
@@ -45,11 +49,11 @@ protected override void Init()
4549 ///
4650 /// </summary>
4751 /// <param name="text"></param>
48- /// <param name="fileName "></param>
49- public override void Render ( string text , string fileName )
52+ /// <param name="fileInfo "></param>
53+ public override void Render ( string text , FileInformation fileInfo )
5054 {
51- base . Render ( text , fileName ) ;
52- this . markdownViewerHtmlPanel . Text = BuildHtml ( ConvertedText , fileName ) ;
55+ base . Render ( text , fileInfo ) ;
56+ this . markdownViewerHtmlPanel . Text = BuildHtml ( ConvertedText , fileInfo . FileName ) ;
5357 }
5458
5559 /// <summary>
@@ -61,5 +65,126 @@ public override void ScrollByRatioVertically(double scrollRatio)
6165 {
6266 this . markdownViewerHtmlPanel . ScrollByRatioVertically ( scrollRatio ) ;
6367 }
68+
69+ /// <summary>
70+ /// Custom renderer for SVG images in the markdown as not supported natively.
71+ /// @see https://htmlrenderer.codeplex.com/wikipage?title=Rendering%20SVG%20images
72+ /// </summary>
73+ /// <param name="sender"></param>
74+ /// <param name="imageLoadEvent"></param>
75+ protected void OnImageLoad ( object sender , HtmlImageLoadEventArgs imageLoadEvent )
76+ {
77+ try
78+ {
79+ //Get some file information
80+ string src = imageLoadEvent . Src ;
81+ Uri uri = new Uri ( src ) ;
82+ string extension = Path . GetExtension ( src ) ;
83+
84+ //Check if local file or web resource
85+ switch ( uri . Scheme . ToLowerInvariant ( ) )
86+ {
87+ case "file" :
88+ //In case of a local file -> Try to load it directly
89+ imageLoadEvent . Handled = true ; //Tell the event it was handled, so no error border is drawn
90+ ThreadPool . QueueUserWorkItem ( state => LoadImageFromFile ( src , imageLoadEvent ) ) ;
91+ break ;
92+ case "http" :
93+ case "https" :
94+ //For web resources check extension and parameter, to fetch from e.g. "badge" creating sources
95+ if ( ( extension != null && extension . Equals ( ".svg" , StringComparison . OrdinalIgnoreCase ) )
96+ || uri . ToString ( ) . Contains ( "svg=" ) )
97+ {
98+ //In case of a web resource file -> Load async
99+ using ( WebClient webClient = new WebClient ( ) )
100+ {
101+ imageLoadEvent . Handled = true ; //Tell the event it was handled, so no error border is drawn
102+ webClient . DownloadDataCompleted += ( downloadSender , downloadEvent ) => { OnDownloadDataCompleted ( downloadEvent , imageLoadEvent ) ; } ;
103+ webClient . DownloadDataAsync ( uri ) ;
104+ }
105+ }
106+ break ;
107+ }
108+
109+ }
110+ catch
111+ {
112+ }
113+ }
114+
115+ /// <summary>
116+ ///
117+ /// </summary>
118+ /// <param name="src"></param>
119+ /// <param name="imageLoadEvent"></param>
120+ protected void LoadImageFromFile ( string src , HtmlImageLoadEventArgs imageLoadEvent )
121+ {
122+ try
123+ {
124+ Uri uri = new Uri ( src ) ;
125+ //Try to load the file as Image from file
126+ //Remove the scheme first
127+ string srcWithoutScheme = src ;
128+ int i = srcWithoutScheme . IndexOf ( ':' ) ;
129+ if ( i > 0 ) srcWithoutScheme = srcWithoutScheme . Substring ( i + 1 ) . TrimStart ( '/' ) ;
130+ //If not absolute, add the current file path
131+ if ( ! Path . IsPathRooted ( srcWithoutScheme ) )
132+ {
133+ uri = new Uri ( @"file:///" + this . FileInfo . FileDirectory + "/" + srcWithoutScheme ) ;
134+ }
135+
136+ //For SVG images: Convert to Bitmap
137+ string extension = Path . GetExtension ( src ) ;
138+ if ( extension != null && extension . Equals ( ".svg" , StringComparison . OrdinalIgnoreCase ) )
139+ {
140+ ConvertSvgToBitmap ( SvgDocument . Open < SvgDocument > ( uri . LocalPath ) , imageLoadEvent ) ;
141+ }
142+ else
143+ {
144+ //Load uri, 8, 1
145+ imageLoadEvent . Callback ( ( Bitmap ) Image . FromFile ( uri . LocalPath , true ) ) ;
146+ }
147+ }
148+ catch { } //Not able to handle, refer back to orginal process
149+ }
150+
151+ /// <summary>
152+ ///
153+ /// </summary>
154+ /// <param name="svgDocument"></param>
155+ /// <param name="imageLoadEvent"></param>
156+ protected Bitmap ConvertSvgToBitmap ( SvgDocument svgDocument , HtmlImageLoadEventArgs imageLoadEvent )
157+ {
158+ Bitmap svgImage = new Bitmap ( ( int ) svgDocument . Width , ( int ) svgDocument . Height , PixelFormat . Format32bppArgb ) ;
159+ svgDocument . Draw ( svgImage ) ;
160+ imageLoadEvent . Callback ( svgImage ) ;
161+ imageLoadEvent . Handled = true ;
162+ return svgImage ;
163+ }
164+
165+ /// <summary>
166+ ///
167+ /// </summary>
168+ /// <param name="downloadEvent"></param>
169+ /// <param name="imageLoadEvent"></param>
170+ protected void OnDownloadDataCompleted ( DownloadDataCompletedEventArgs downloadEvent , HtmlImageLoadEventArgs imageLoadEvent )
171+ {
172+ using ( MemoryStream stream = new MemoryStream ( downloadEvent . Result ) )
173+ {
174+ ConvertSvgToBitmap ( SvgDocument . Open < SvgDocument > ( stream ) , imageLoadEvent ) ;
175+ }
176+ }
177+
178+ /// <summary>
179+ /// Release the custom loader
180+ /// </summary>
181+ protected override void Dispose ( bool disposing )
182+ {
183+ if ( this . markdownViewerHtmlPanel != null )
184+ {
185+ this . markdownViewerHtmlPanel . ImageLoad -= OnImageLoad ;
186+ }
187+ base . Dispose ( disposing ) ;
188+ }
64189 }
65190}
0 commit comments