一年前我寫了一個word2010的代碼高亮插件,但當時那個版本有一個問題:在用word發布博客的時候,高亮的代碼在博客中的格式亂了。今天有空改了一下這個插件,雖然還是有些瑕疵,但至少發布到博客后,格式不會亂了。主要改進是用ol和li代替了pre,雖然發布到博客后,ol中設置的style和class依然會被改動,但可以在博客中用css來糾正。
下載插件和源代碼:SyntaxHighlighter4Word.zip
下面說一下這個插件的用法。
下載文件后,解壓,然后雙擊bin\word2010\Kong.SyntaxHighlighter.Word2010.vsto或bin\word2007\Kong.SyntaxHighlighter.Word2007.vsto,即可完成安裝,當然前提是你裝了.net framework 4.0。安裝成功后的提示如下:
安裝插件后,會在word中多一個功能區(支持word2007和word2010):
點擊"設置"按鈕,彈出設置界面:
這里簡化了配置,去掉了前一個版本中的一些設置。
點擊"插入代碼"按鈕,彈出如下界面:
可以選擇C#、Java、Xml、Javascript等多種語言。
在word效果如下:
發布到博客后的效果如下:
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
using System.Windows.Forms;
-
using Kong.SyntaxHighlighter.Winform;
-
using Microsoft.Office.Tools.Ribbon;
-
-
namespace Kong.SyntaxHighlighter.Word
-
{
-
public partial class Ribbon1
-
{
-
private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
-
{
-
-
}
-
}
-
}
我在Word中生成這段代碼的時候,用了ol和li,並且設置了ol以及li的style,這樣在word中就可以顯示邊框以及交替行的顏色,同時給ol設了一個class=codeBlock,妄想在發布到博客后可以通過這個樣式名codeBlock來自定義自己喜歡的樣式。我在word中生成的代碼大概是這個樣子:
-
<ol class="codeBlock" ...
但是word把這段代碼發布到博客后,會去除掉這個class,無語。。。
所以我們在博客中,不得設置所有ol的style,幸好博客園的文章都是在一個id為cnblogs_post_body的div下的,所以我在我博客中加了下面的style:
-
#cnblogs_post_body ol
-
{
-
border: 1px dotted #000066;
-
line-height: 150%;
-
word-break: break-word;
-
font-family: Consolas, Verdana !important;
-
border-radius: 5px;
-
width: 90%;
-
background-color: #E3E3FF;
-
list-style-position: outside;
-
margin-left: 0px;
-
}
-
#cnblogs_post_body ol font
-
{
-
font-size: 12px !important;
-
}
-
#cnblogs_post_body ol li
-
{
-
background-color: #fff;
-
padding-left: 5px;
-
border-left: 1px solid #8A8AFF;
-
margin-left: 5px !important;
-
}
-
#cnblogs_post_body ol li:nth-child(even)
-
{
-
background-color: #f5f5f5;
-
}
補充一下,這段文本是加在這里的:
插件的使用就介紹到這里,下面簡單介紹一下插件的實現。
如何開發office的add in,園子里已經有很多文章了,我就不介紹了,因為我自己也不懂。
如何實現代碼高亮?我用的是Wilco.SyntaxHighlighting,有興趣的同學可以google一下,我提供的下載包里也有它的源碼。
代碼高亮后,如何粘帖到word里?原理就是把代碼高亮后的文本以html格式復制到剪貼板里,然后調用word的方法去粘帖:
-
private void InsertButton_Click(object sender, RibbonControlEventArgs e)
-
{
-
var dialog = new MainForm();
-
if (dialog.ShowDialog() == DialogResult.OK)
-
{
-
dialog.CopyToClipboard();
-
Globals.ThisAddIn.Application.Selection.Paste();
-
}
-
}
以html格式復制到剪貼板的實現,我是從網上找了一段代碼來做的,核心邏輯如下:
-
public static void CopyToClipboard(string htmlFragment, string title, Uri sourceUrl)
-
{
-
if (title == null) title = "From Clipboard";
-
-
System.Text.StringBuilder sb = new System.Text.StringBuilder();
-
-
string header =
-
@"Format:HTML Format
-
Version:1.0
-
StartHTML:<<<<<<<1
-
EndHTML:<<<<<<<2
-
StartFragment:<<<<<<<3
-
EndFragment:<<<<<<<4
-
StartSelection:<<<<<<<3
-
EndSelection:<<<<<<<3
-
";
-
-
string pre =
-
@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">
-
<HTML><HEAD><TITLE>" + title + @"</TITLE></HEAD><BODY><!--StartFragment-->";
-
-
string post = @"<!--EndFragment--></BODY></HTML>";
-
-
sb.Append(header);
-
if (sourceUrl != null)
-
{
-
sb.AppendFormat("SourceURL:{0}", sourceUrl);
-
}
-
var enc = Encoding.UTF8; //中文亂碼問題
-
int startHTML = enc.GetByteCount(sb.ToString());
-
-
sb.Append(pre);
-
int fragmentStart = enc.GetByteCount(sb.ToString());
-
-
sb.Append(htmlFragment);
-
int fragmentEnd = enc.GetByteCount(sb.ToString());
-
-
sb.Append(post);
-
int endHTML = enc.GetByteCount(sb.ToString());
-
-
// Backpatch offsets
-
sb.Replace("<<<<<<<1", To8DigitString(startHTML));
-
sb.Replace("<<<<<<<2", To8DigitString(endHTML));
-
sb.Replace("<<<<<<<3", To8DigitString(fragmentStart));
-
sb.Replace("<<<<<<<4", To8DigitString(fragmentEnd));
-
-
Clipboard.Clear();
-
var dataObj = new DataObject();
-
dataObj.SetData(DataFormats.Html, new MemoryStream(enc.GetBytes(sb.ToString())));
-
Clipboard.SetDataObject(dataObj, true);
-
}
-
-
#endregion // Write to Clipboard
-
}
這個類名叫做HtmlFragment,可以在我提供的下載包里找到。
另外,我這個插件在生成高亮代碼時,可以清除掉代碼段首尾的空行,也可以清除掉每一行的公共空格,比如下面的代碼:
在插入后會變成這個樣子:
-
private void Test()
-
{
-
var i = 0;
-
//do something
-
}
我用了幾條正則表達式來實現這個功能,代碼如下:
-
private string GetHtml(string content)
-
{
-
_highlighter.Parser = _htmlParser;
-
string html = _highlighter.Parse(content);
-
_highlighter.Parser = _parser;
-
if (html != null)
-
{
-
html = html.Replace("\t", " ");
-
//清除首尾空行
-
html = Regex.Replace(html, @"(^\s*\n)|(\n\s*$)", "", RegexOptions.Singleline);
-
-
//清除掉公共的空格
-
MatchCollection matches = Regex.Matches(html, @"^ *(?=\S)", RegexOptions.Multiline);
-
int len = matches.OfType<Match>().Select(m => m.Value.Length).Min();
-
html = Regex.Replace(html, @"^ {" + len + "}", "", RegexOptions.Multiline);
-
-
//把每一行開頭的空格變成
-
html = Regex.Replace(html, @"^ +(?=\S)",
-
new MatchEvaluator(
-
m => string.Join("", new string[m.Length].Select(s => " "))),
-
RegexOptions.Multiline);
-
}
-
return html;
-
}
有興趣的同學可以下載源碼看一下。