MarkdownRendererForm.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using AngleSharp;
  2. using AngleSharp.Css.Dom;
  3. using AngleSharp.Dom;
  4. using AngleSharp.Html.Parser;
  5. using CefSharpWrap;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Windows.Forms;
  11. namespace MarkdownRenderer
  12. {
  13. public partial class MarkdownRendererForm : Form
  14. {
  15. private readonly CefSharpWrapBrowser _rendererBrowser;
  16. private readonly HtmlParser _agHtmlParser;
  17. private string _html;
  18. private double _scrollPosition;
  19. private string _styleSheet;
  20. private string _bodyClassName;
  21. public MarkdownRendererForm()
  22. {
  23. InitializeComponent();
  24. _rendererBrowser = CefSharpWrapFactory.CreateSealedInstance($"{CefSharpWrapFactory.SchemeName}://{CefSharpWrapFactory.CustomResourceDomainName}/{MarkdownRendererMain.CustomResourcePath}/");
  25. _rendererBrowser.FillToControl(MainPanel);
  26. _rendererBrowser.RegisterJsObject("BridgeObject", new RenderJsObjectBridge(this));
  27. _rendererBrowser.ExternalRequested += delegate (object sender, string fileName)
  28. {
  29. try
  30. {
  31. Process.Start(fileName);
  32. }
  33. catch (Exception e)
  34. {
  35. MessageBox.Show(e.ToString(), "External execute fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
  36. }
  37. };
  38. var agContext = BrowsingContext.New(Configuration.Default.WithDefaultLoader().WithCss());
  39. _agHtmlParser = new HtmlParser(new HtmlParserOptions(), agContext);
  40. FillStyleSheetDropDownMenuItems(BuiltInStyleSheetsBulidinToolStripMenuItem, StyleSheetStore.BuiltInEntries);
  41. RefreshCustomStyleSheetMenuItems();
  42. //TODO: Active last used style sheet
  43. SetStyleSheet(StyleSheetStore.BuiltInEntries[0]);
  44. }
  45. private void SetStyleSheet(StyleSheetEntry styleSheet)
  46. {
  47. _styleSheet = styleSheet.Css;
  48. _bodyClassName = styleSheet.BodyClass;
  49. _rendererBrowser.ExecuteJsAsync("syncStyle()");
  50. }
  51. public void PutMarkdown(string mdText)
  52. {
  53. var action = new Action(() =>
  54. {
  55. _html = MarkdownHtmlConverter.ToHtml(mdText);
  56. _rendererBrowser.ExecuteJsAsync("syncHtml()");
  57. });
  58. if (InvokeRequired) Invoke(action);
  59. else action();
  60. }
  61. public void ScrollToPercent(double scrollPosition)
  62. {
  63. //TODO: Sync scroll position match to section. too hard to implement
  64. _scrollPosition = scrollPosition;
  65. _rendererBrowser.ExecuteJsAsync("syncScroll()");
  66. }
  67. private void RefreshCustomStyleSheetMenuItems()
  68. {
  69. for (var i = CustomStyleSheetNoneToolStripMenuItem.DropDownItems.Count - 1; i >= 0; i--)
  70. {
  71. var item = CustomStyleSheetNoneToolStripMenuItem.DropDownItems[i];
  72. if (item != CustomStyleSheetNoneToolStripMenuItem) CustomStyleSheetNoneToolStripMenuItem.DropDownItems.Remove(item);
  73. }
  74. var entries = StyleSheetStore.GetEntries();
  75. if (false == (CustomStyleSheetNoneToolStripMenuItem.Visible = entries.Length == 0))
  76. {
  77. FillStyleSheetDropDownMenuItems(BuiltInStyleSheetsBulidinToolStripMenuItem, entries);
  78. }
  79. }
  80. private static void FillStyleSheetDropDownMenuItems(ToolStripDropDownItem container, IEnumerable<StyleSheetEntry> entries)
  81. {
  82. foreach (var entry in entries)
  83. {
  84. var item = new ToolStripMenuItem
  85. {
  86. Text = entry.Name,
  87. Tag = entry
  88. };
  89. //TODO: restore check state
  90. container.DropDownItems.Add(item);
  91. }
  92. }
  93. private string GenerateHtmlForExport()
  94. {
  95. var htmlTemplate = Properties.Resources.TemplatePageForExport;
  96. var doc = _agHtmlParser.ParseDocument(htmlTemplate);
  97. doc.QuerySelector("style").InnerHtml = _styleSheet;
  98. var body = doc.QuerySelector("body");
  99. body.InnerHtml = _html;
  100. body.ClassName = _bodyClassName;
  101. using (var writer = new StringWriter())
  102. {
  103. doc.ToHtml(writer);
  104. return writer.ToString();
  105. }
  106. }
  107. private void CopyToolStripButton_Click(object sender, EventArgs e)
  108. {
  109. try
  110. {
  111. var html = GenerateHtmlForExport();
  112. Clipboard.SetText(html);
  113. }
  114. catch (Exception exception)
  115. {
  116. MessageBox.Show(exception.ToString(), "Copy HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
  117. }
  118. }
  119. private void CopyInlineStyleHtml_Click(object sender, EventArgs e)
  120. {
  121. try
  122. {
  123. var html = GenerateHtmlForExport();
  124. var doc = _agHtmlParser.ParseDocument(html);
  125. //TODO: check why pre and quote missing background-image
  126. foreach (var element in doc.QuerySelectorAll("body,body *"))
  127. {
  128. var style = doc.DefaultView.GetComputedStyle(element);
  129. element.SetStyle(style.CssText.Replace('\"', '\''));
  130. element.RemoveAttribute("class");
  131. }
  132. foreach (var element in doc.QuerySelectorAll("style"))
  133. {
  134. element.Remove();
  135. }
  136. var writer = new StringWriter();
  137. doc.ToHtml(writer);
  138. var processedHtml = writer.ToString();
  139. Clipboard.SetText(processedHtml);
  140. }
  141. catch (Exception exception)
  142. {
  143. MessageBox.Show(exception.ToString(), "Copy HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
  144. }
  145. }
  146. private void SaveToolStripButton_Click(object sender, EventArgs e)
  147. {
  148. var sfd = new SaveFileDialog
  149. {
  150. Title = "Save as HTML",
  151. Filter = "HTML files (*.html;*.htm)|*.htm;*.html"
  152. };
  153. if (DialogResult.OK != sfd.ShowDialog()) return;
  154. try
  155. {
  156. var html = GenerateHtmlForExport();
  157. File.WriteAllText(sfd.FileName, html);
  158. }
  159. catch (Exception exception)
  160. {
  161. MessageBox.Show(exception.ToString(), "Save as HTML fail", MessageBoxButtons.OK, MessageBoxIcon.Error);
  162. }
  163. }
  164. private void PrintToolStripButton_Click(object sender, EventArgs e)
  165. {
  166. new PrintToPdfForm(_rendererBrowser).ShowDialog();
  167. }
  168. private void StyleSheetManageToolStripMenuItem_Click(object sender, EventArgs e)
  169. {
  170. new StyleSheetForm().ShowDialog();
  171. //TODO: if current active entry was delete (not found in custom and bulit-in), choose an other one or reset to first built-in
  172. RefreshCustomStyleSheetMenuItems();
  173. }
  174. private void HelpToolStripButton_Click(object sender, EventArgs e)
  175. {
  176. new AboutForm().ShowDialog();
  177. }
  178. private void F12ToolStripButton_Click(object sender, EventArgs e)
  179. {
  180. _rendererBrowser.ShowF12();
  181. }
  182. private void MarkdownRendererForm_FormClosed(object sender, FormClosedEventArgs e)
  183. {
  184. _rendererBrowser.Dispose();
  185. }
  186. private void StyleSheetsToolStripMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
  187. {
  188. foreach (ToolStripMenuItem item in CustomStyleSheetNoneToolStripMenuItem.DropDownItems) item.Checked = false;
  189. foreach (ToolStripMenuItem item in BuiltInStyleSheetsBulidinToolStripMenuItem.DropDownItems) item.Checked = false;
  190. var clickedItem = (ToolStripMenuItem)e.ClickedItem;
  191. clickedItem.Checked = true;
  192. SetStyleSheet((StyleSheetEntry)clickedItem.Tag);
  193. //TODO: Save currelt selected style sheet id to config store
  194. }
  195. private class RenderJsObjectBridge
  196. {
  197. private readonly MarkdownRendererForm _formInstance;
  198. public RenderJsObjectBridge(MarkdownRendererForm formInstance) => _formInstance = formInstance;
  199. // ReSharper disable UnusedMember.Local
  200. public string StyleSheet => _formInstance._styleSheet;
  201. public string BodyClassName => _formInstance._bodyClassName;
  202. public string Html => _formInstance._html;
  203. public double ScrollPosition => _formInstance._scrollPosition;
  204. // ReSharper restore UnusedMember.Local
  205. }
  206. }
  207. }