MarkWord - 可發佈博客的 Markdown編輯器 代碼開源

因爲前一段時間看到 NetAnalyzer 在Windows10系統下UI表現慘不忍睹,所以利用一段時間爲了學習一下WPF相關的內容,於是停停寫寫,用了WPF相關的技術,兩個星期做了一個Markdown編輯器,並且集成了:編輯時與網頁同步,博客發佈,PDF導出等功能。也主要是不忿某款外國軟件收費,故有此作。

代碼下載地址

https://github.com/Twzy/MarkWord

展示與說明

代碼同步編輯

img

博客發佈

img

代碼說明

博客發佈

MarkWord支持博客園和CSDN博客發佈,並且可以進行圖片同步(無論是本地圖片還是網上的圖片,都可以同步到博客服務器)。 該功能使用了MetaWeblog技術。使用方法如下,其中圖片上傳爲newMediaObject 接口

  1 /// <summary>
  2 /// 文檔上傳,包括新增與更新
  3 /// </summary>
  4 public static string UploadBlogs(string apiUrl, string BlogId, string userId, string password, string 
  5 BlogsModel, string postId, string title, string Markdown, bool publish)
  6 {
  7 
  8     int procIndex = 1;
  9 
 10     SendMsg(5, procIndex, "準備數據中……");
 11     //轉換爲html
 12     string Blogs = string.Format("<!-- edit by MarkWord 墨雲軟件 -->\r\n{0}", 
 13     CommonMark.CommonMarkConverter.Convert(Markdown));
 14     metaTools.Url = apiUrl;
 15 
 16 
 17     Post blogsPost = new Post();
 18 
 19     //分類
 20     List<string> tmpCategories = new List<string>();
 21     tmpCategories.Add("");//添加空分類,是因爲部分博客(如csdn)字段這部分爲必填字段不添加會產生異常
 22     blogsPost.categories = tmpCategories.ToArray();
 23 
 24     //添加時間
 25     blogsPost.dateCreated = DateTime.Now.ToLocalTime();
 26 
 27     //添加標題
 28     blogsPost.title = title;
 29 
 30 
 31     //指定文章編號
 32     blogsPost.postid = postId;
 33 
 34     //內容
 35     blogsPost.description = BlogsModel.Contains("{0}") ?//必須使用{0}佔位符
 36         string.Format(BlogsModel, Blogs) : //根據模板生成數據 主要是爲了制定Markdown模板
 37         BlogsModel + Blogs; //通過前綴方式添加
 38 
 39     //開始查找圖片並更新到服務器
 40     HtmlDocument htmlDoc = new HtmlDocument();
 41     WebClient webClient = new WebClient();
 42     htmlDoc.LoadHtml(blogsPost.description);
 43     var ImgList = htmlDoc.DocumentNode.Descendants("img");
 44 
 45     int procCount = 3 + ImgList.Count();
 46 
 47     SendMsg(procCount, procIndex++, string.Format("數據分析完成,總共需要上傳{0}張圖片", ImgList.Count()));
 48     int imgErr = 0;//圖片上傳錯誤數量
 49     foreach (var i in ImgList)
 50     {
 51         SendMsg(procCount, procIndex++, "正在上傳圖片數據……");
 52         //獲取圖片文件字符串
 53         string ImgUrl = i.GetAttributeValue("src", "");
 54         if (string.IsNullOrEmpty(ImgUrl))
 55         {
 56             imgErr++;
 57             continue;
 58         }
 59         try
 60         {
 61             var imgeData = webClient.DownloadData(ImgUrl);//下載文件
 62 
 63             FileData fd = default(FileData);
 64             fd.bits = imgeData;//圖片數據
 65             fd.name = Path.GetExtension(ImgUrl);//文件名
 66             fd.type = string.Format("image/{0}", fd.name.Substring(1));
 67 
 68             UrlData obj = metaTools.newMediaObject(BlogId, userId, password, fd);
 69             blogsPost.description = blogsPost.description.Replace(ImgUrl, obj.url);
 70         }
 71         catch
 72         {
 73             imgErr++;
 74             continue;
 75         }
 76     }
 77     try
 78     {
 79         if (string.IsNullOrWhiteSpace(postId))
 80         {
 81             SendMsg(procCount, procIndex++, "開始發佈文章……");
 82             postId = metaTools.newPost(BlogId, userId, password, blogsPost, publish);
 83         }
 84         else
 85         {
 86             SendMsg(procCount, procIndex++, "正在更新文章……");
 87             metaTools.editPost(postId, userId, password, blogsPost, publish);
 88         }
 89     }
 90     catch (Exception ex)
 91     {
 92         Common.ShowMessage("博客發送失敗");
 93         return postId;
 94     }
 95 
 96     if (imgErr == 0)
 97     {
 98         Common.ShowMessage("博客發送成功");
 99     }
100     else
101     {
102         Common.ShowMessage(string.Format("博客發送成功了,但是有{0}張圖片發送失敗", imgErr));
103     }
104     SendMsg(procCount, procCount, "完成");
105     return postId;
106 
107 }

 

具體API實現方法見代碼中的BlogsAPI項目

PDF導出

PDF導出功能,使用了HTML轉PDF方法 相關DLL已經包含在項目當中了

 1 //html to Pdf
 2 public static void HtmlToPdf(string filePath, string html, bool isOrientation = false)
 3 {
 4     if (string.IsNullOrEmpty(html))
 5         html = "Null";
 6     // 創建全局信息
 7     GlobalConfig gc = new GlobalConfig();
 8     gc.SetMargins(new Margins(50, 50, 60, 60))
 9         .SetDocumentTitle("MarkWord")
10         .SetPaperSize(PaperKind.A4)
11         .SetPaperOrientation(isOrientation)
12         .SetOutlineGeneration(true);
13 
14            
15     //頁面信息
16     ObjectConfig oc = new ObjectConfig();
17     oc.SetCreateExternalLinks(false)
18         .SetFallbackEncoding(Encoding.UTF8)
19         .SetLoadImages(true)
20         .SetScreenMediaType(true)
21         .SetPrintBackground(true);
22     //.SetZoomFactor(1.5);
23 
24     var pechkin = new SimplePechkin(gc);
25     pechkin.Finished += Pechkin_Finished;
26     pechkin.Error += Pechkin_Error;
27     pechkin.ProgressChanged += Pechkin_ProgressChanged;
28     var buf = pechkin.Convert(oc, html);
29 
30     if (buf == null)
31     {
32         Common.ShowMessage("導出異常");
33         return;
34     }
35 
36     try
37     {
38         string fn = filePath; //Path.GetTempFileName() + ".pdf";
39         FileStream fs = new FileStream(fn, FileMode.Create);
40         fs.Write(buf, 0, buf.Length);
41         fs.Close();
42 
43         //Process myProcess = new Process();
44         //myProcess.StartInfo.FileName = fn;
45         //myProcess.Start();
46     }
47     catch { }
48 }

 

CommonMark使用

最後就Markdown的轉換,在這裏我使用了CommonMark,使用方法比較簡單

CommonMark.CommonMarkConverter.Convert("### test")

 

編輯與html頁面同步原理 

在改工具中比較有意思的就是編輯器與Webbrower的頁面同步功能,包括頁面"無刷新"同步呈現,已經頁面同步滾動,在這裏使用的是編輯器觸發 textEditor_TextChanged 事件和 ScrollViewer 觸發的scrViewer_ScrollChanged 分別通過webbrowser 的 InvokeScript 動態調用Js實現的,我們先來看看兩個js的內容

同步呈現

function updatePageContent(msg){ 
document.body.innerHTML= msg;
} 

非常簡單,只是將轉換出來的html直接通過document.body.innerHTML 賦給當前頁面既可以了。

同步滾動

function scrollToPageContent(value){ 
  window.scrollTo(0, value * (document.body.scrollHeight - document.body.clientHeight));
 } 

這部分,是需要通過WPF頁面轉過來一個對應的頁面位移高度與窗口顯示高度的一個頁面比例,然後在webbrowser中根據該比例計算頁面需要偏移量來實現同步移動

而對應的WPF端的代碼爲

 1         /// <summary>
 2         /// 同步呈現
 3         /// </summary>
 4         /// <param name="value"></param>
 5         public void LoadBody(string MarkValue)
 6         {
 7 
 8             if (winWebDoc.Document == null)
 9                 return;
10             winWebDoc.Document.InvokeScript("updatePageContent", new object[] { CommonMark.CommonMarkConverter.Convert(MarkValue) });
11         }
12 
13         /// <summary>
14         /// 文本更變
15         /// </summary>
16         /// <param name="sender"></param>
17         /// <param name="e"></param>
18         private void textEditor_TextChanged(object sender, EventArgs e)
19         {
20             if (!isLoadFlag)
21             {
22                 if (this.textEditor.Text != "" && scrViewer != null)
23                     if (scrViewer.ScrollableHeight == scrViewer.VerticalOffset)
24                         scrViewer.ScrollToBottom();
25 
26                 BLL.FileManager.isChangeFlag = true;
27             }
28             //加載文檔
29             if (MarkDoc == null)
30                 return;
31             if (Config.Common.WorkType == WorkType.Both)
32             {
33                 MarkDoc.LoadBody(this.textEditor.Text);
34             }
35         }
36        //////////////////////////////////////////////////////////////////////////////////
37         /// <summary>
38         /// 同步滾動
39         /// </summary>
40         /// <param name="value"></param>
41         public void ScrollAuto(double value)
42         {
43             if (winWebDoc.Document == null)
44                 return;
45             winWebDoc.Document.InvokeScript("scrollToPageContent", new object[] { value.ToString(System.Globalization.CultureInfo.InvariantCulture) });
46            
47         }
48         //計算比例
49         public double ScrollViewerPositionPercentage
50         {
51             get
52             {
53                 double num = this.scrViewer.ExtentHeight - this.scrViewer.ViewportHeight;
54                 double result;
55                 if (num != 0.0)
56                 {
57                     result = this.scrViewer.VerticalOffset / num;
58                 }
59                 else
60                 {
61                     result = 0.0;
62                 }
63                 return result;
64             }
65         }
66 
67         //觸發同步
68         private void scrViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
69         {
70             if (MarkDoc == null) return;
71             if (Config.Common.WorkType == WorkType.Both)
72             {
73                 MarkDoc.ScrollAuto(this.ScrollViewerPositionPercentage);
74             }
75         }

至此,Markword 中設計到的內容點已經基本覆蓋到了,如有疑問歡迎交流!!!

 

 


 

最後來一發小廣告

NetAnalyzer2016網絡協議分析軟件源碼開放購買,可以分析80多種協議,支持http數據還原(包含chunked和gzip數據) ,歡迎大家可以支持一下!!

墨雲NetAnalyzer官網
代碼購買鏈接
如有疑問歡迎QQ聯繫:470200051

祝大家週末愉快

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章