using AIProofread.Controls; using AIProofread.core; using AIProofread.Util; using Microsoft.Office.Interop.Word; using Microsoft.Office.Tools.Word; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Forms; using UtilLib; using Document = Microsoft.Office.Interop.Word.Document; using Task = System.Threading.Tasks.Task; namespace AIProofread { public enum BridgeEvent { LoginSuccess, WindowClose } public delegate void BridgeEventHandler(object sender, EventArgs e); [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class Bridge { public static Bridge bridge = new Bridge(); private static readonly Dictionary webViewDict = new Dictionary(); private static readonly Dictionary> eventHandlers = new Dictionary>(); public static Dictionary marks; private static int selectProofreadId = -1; private static object missing = System.Reflection.Missing.Value; private static UpgradeData CurrentUpgrade = null; public void ShowUpgradeView() { var needUpgrade = CurrentUpgrade.NeedUpgrade(Config.APP_VERSION); if (CurrentUpgrade.Ext == 1) { if (!needUpgrade) { showDialog("当前版本为最新版本,无需升级"); } else { var ret = MessageBox.Show(CurrentUpgrade.Message, "是否确认更新", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question); if (ret == DialogResult.Yes) { OpenUrlWithOsBrowser(CurrentUpgrade.DownloadUrl); } } } else { StartUpgradeProcess(); } } public bool ShouldUpgradeForced() { return CurrentUpgrade != null && CurrentUpgrade.NeedUpgrade(Config.APP_VERSION) && CurrentUpgrade.UpgradeType == 1; } public void InitPluginUpgrade() { try { string source = HttpUtil.GetHttpSource(Config.WEB_PATH + "api/v1/common/download/version"); if (source == null) return; UpgradeSource data = JsonConvert.DeserializeObject(source); if (data == null || data.Code != 0) return; CurrentUpgrade = data.Data; // 是否需要强制升级 //if (ShouldUpgradeForced()) //{ // // 显示升级框 // ShowUpgradeView(); //} } catch (Exception ex) { Logger.Log(ex); } } public string GetAppVersion() { return Config.APP_VERSION; } public void showDialog(string message) { System.Windows.Forms.MessageBox.Show(message); } public int getMinWIdth() { return Globals.ThisAddIn.GetMinWidth(); } /// /// 添加事件 /// /// /// public static void AddEventHandler(BridgeEvent eventName, BridgeEventHandler handler) { if (!eventHandlers.ContainsKey(eventName)) { eventHandlers.Add(eventName, new List()); } eventHandlers[eventName].Add(handler); } public static void RemoveEventHandler(BridgeEvent eventName, BridgeEventHandler handler) { if (eventHandlers.ContainsKey(eventName)) { eventHandlers[eventName].Remove(handler); } } public void SetCurrentDocumentId(int id) { SetDocumentId(id, Globals.ThisAddIn.Application.ActiveDocument); } public void SetDocumentId(int id, Document document) { Globals.ThisAddIn.documentIdDics[document] = id; } public Document GetDocumentById(int id) { //return GeIdBytDocument(Globals.ThisAddIn.Application.ActiveDocument); var documentDic = Globals.ThisAddIn.documentIdDics; foreach (var doc in documentDic.Keys) { if (documentDic[doc] == id) return doc; } return null; } public int GetCurrentDocumentId() { return GeIdBytDocument(Globals.ThisAddIn.Application.ActiveDocument); } public int GeIdBytDocument(Document document) { if (Globals.ThisAddIn.documentIdDics.ContainsKey(document)) { return Globals.ThisAddIn.documentIdDics[document]; } //int id = Globals.ThisAddIn.documentIdDics.Count + 1; //// 设置文档编号 //SetCurrentDocumentId((int)id); //return id ; return 0; } // 打开网页 public void OpenUrlWithOsBrowser(string url) { try { Process.Start(url); } catch { showDialog("打开网页失败"); } } public static void StartUpgradeProcess() { try { string applicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; string path = "updater.exe"; ProcessStartInfo processStartInfo = new ProcessStartInfo(Path.Combine(applicationBase, Path.GetFileName(path))) { WorkingDirectory = applicationBase, }; Process.Start(processStartInfo); } catch (Exception e) { Logger.Log(e); MessageBox.Show("启动升级程序失败,请重试"); } } public void StartProofread() { Globals.ThisAddIn.SendMessageToWeb("start", "login"); } public void ShowCurrentPane() { Globals.ThisAddIn.ShowPanel(); //Globals.ThisAddIn.currentDocumentTaskPane.Visible = true; } public void HideCurrentPane() { Globals.ThisAddIn.HidePanel(); //Globals.ThisAddIn.currentDocumentTaskPane.Visible = false; } public void Logout(string action) { if (action == "async-info") { // web同步注销到ribbon Globals.ThisAddIn.SyncLogout(); } else { // ribbon 发送注销动作到 web Globals.ThisAddIn.SendMessageToWeb("logout", null); } } /// /// 登录成功后通知后端 /// public void loginSuccess(string userinfo) { if (userinfo == null || userinfo.Length == 0) return; // Userinfo info = JsonConvert.DeserializeObject(userinfo); // 登录成功 展示 Globals.ThisAddIn.ribbon.ProcessLoginInfo(info); //Globals.ThisAddIn.SendMessageToWeb("async-login-success", null); } // 获取文档所有文本数据 public Dictionary getAllText() { return Tools.GetAllText(Globals.ThisAddIn.Application.ActiveDocument); } public string getDocumentData() { Dictionary data = new Dictionary(); var doc = Globals.ThisAddIn.Application.ActiveDocument; if (!doc.Saved) { data.Add("code", 1); data.Add("message", "请保存文档后再进行校对"); } else if (ShouldUpgradeForced()) { data.Add("code", 2); data.Add("message", "请升级插件后再进行校对"); } else if (doc.ProtectionType != WdProtectionType.wdNoProtection) { data.Add("code", 3); data.Add("message", "文档受保护,无法编辑"); } else { data.Add("code", 0); data.Add("message", "success"); data.Add("name", doc.Name); data.Add("fullName", doc.FullName); //data.Add("documentId", GeIdBytDocument(doc)); data.Add("wordsCount", doc.Words.Count); data.Add("charactersCount", doc.Characters.Count); data.Add("content", Tools.GetAllText(doc)); } return Tools.GetJSONString(data); } public string getParagraphTextByRange(int start, int end) { var list = Tools.GetTextListByParagraphRange(start, end); return Tools.GetJSONString(list); } public string getAllParagraphs() { var doc = Globals.ThisAddIn.Application.ActiveDocument; var rangeText = doc.Content.Text; var trimText = HostHelper.ReplaceSpecialChars(rangeText, isReplaceMultSpaceLine: true); string[] separator = new string[5] { "\r\a", "\a", "\r", "\v", "\f" }; string[] array4 = rangeText.Split(separator, StringSplitOptions.None); string[] array5 = trimText.Split('\n'); List list = new List(); var paragraphs = doc.Paragraphs; int total = paragraphs.Count; for (int i = 1; i <= total; i++) { list.Add(GetParagraphText(paragraphs[i])); } var array6 = list.ToArray(); Dictionary data = new Dictionary { {"origin_cut",array4 }, {"trim_cut",array5 }, {"paragraph_cut",array6 }, }; return Tools.GetJSONString(data); } private string GetParagraphText(Paragraph paragraph) { // 需要 return GetRangeText(paragraph.Range); } private string GetRangeText(Range range) { // 需要 return range.Text; } public void getParagraphTextByRangeSync(int start, int end) { //var list = Tools.GetTextListByParagraphRange(start, end); //return Tools.GetJSONString(list); Task.Run(() => { var list = Tools.GetTextListByParagraphRange(start, end); Globals.ThisAddIn.SendMessageToWeb("getParagraphTextByRange", Tools.GetJSONString(list)); }); } public int getTotalParagraphNumber() { return Globals.ThisAddIn.Application.ActiveDocument.Paragraphs.Count; } /// /// 读取文档原始文件并转换成base64 /// /// public string getDocumentFileData() { var doc = Globals.ThisAddIn.Application.ActiveDocument.FullName; FileStream fs = new FileStream(doc, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] bytes = new byte[fs.Length]; fs.Read(bytes, 0, bytes.Length); return Convert.ToBase64String(bytes); } public void ShowUpgrade(string data) { //var upgradeData = JsonConvert.DeserializeObject(data); //var needUpgrade = upgradeData.NeedUpgrade(Config.APP_VERSION); //if (upgradeData.Ext == 1) //{ // if (!needUpgrade) // { // showDialog("当前版本为最新版本,无需升级"); // } // else // { // var ret = MessageBox.Show(upgradeData.Message, "是否确认更新", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question); // if (ret == DialogResult.Yes) // { // OpenUrlWithOsBrowser(upgradeData.DownloadUrl); // } // } //} //else //{ // StartUpgradeProcess(); //} } public void noticeOtherWeb(string json, string targetWebName) { if (targetWebName != null) { if (webViewDict.ContainsKey(targetWebName)) { SendMessageToWeb(webViewDict[targetWebName], json); } } else { // 如果没有指定 则向所有 webview 发送消息 foreach (var item in webViewDict) { SendMessageToWeb(item.Value, json); } } } private void SendMessageToWeb(WebView2 webView, string json) { webView.CoreWebView2.PostWebMessageAsJson(json); } public async static void InitWebEnvAsync(string name, WebView2 webView) { try { webView.Name = name; if (webViewDict.ContainsKey(webView.Name)) { webViewDict[name] = webView; } else { webViewDict.Add(name, webView); } // 禁用web安全,允许跨域 否则需要web编译为umd加载模式 var ops = new CoreWebView2EnvironmentOptions("--disable-web-security"); var env = await CoreWebView2Environment.CreateAsync(null, Config.WEB_DATA_PATH, ops); await webView.EnsureCoreWebView2Async(env); //webView.CoreWebView2.Settings.AreDevToolsEnabled = false; //webView.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; //webView.CoreWebView2.Settings.AreHostObjectsAllowed = true; // 添加 js与客户端代理 webView.CoreWebView2.AddHostObjectToScript("bridge", bridge); } catch (Exception ex) { Logger.Log(ex); } } public void clearAllProofreadMarkById(int documentId) { clearAllProofreadMark(GetDocumentById(documentId)); } // 清除所有标记 public void clearAllProofreadMark(Document document) { // 标记当前文档所有书签数据 Globals.ThisAddIn.ActiveCurrentDocumentMarks(document); try { selectProofreadId = -1; foreach (var item in marks.Values) { if (item.mark != null) { if (item.content.tag == "i" && item.content.isAccept == AcceptStatus.Default) { item.mark.Text = ""; } item.ResetMarkStyle(); } } DocumentUtil.ClearProofreadMarks(); } catch (Exception ex) { Logger.Log("ClearAllProofreadMark", ex); } try { // 清空marks marks.Clear(); } catch (Exception) { } } public void removeBookmark(string markId) { DocumentUtil.RemoveBookmark(markId); } //public void addBookmark(string data) //{ // var item = JsonConvert.DeserializeObject(data); // //var mark = DocumentUtil.AddBookmark(item.color, item.start, item.end); // // 初始化校对对象 // marks.Add(item.id, new ProofreadItem(item)); //} public string getAllBookmark() { return ToJSON(DocumentUtil.GetAllBookmark()); } public string getSectionText() { return ToJSON(DocumentUtil.GetSectionText()); } private static string ToJSON(object data) { return JsonConvert.SerializeObject(data); } public string GetCheckText() { var document = Globals.ThisAddIn.Application.ActiveDocument; int count = Globals.ThisAddIn.Application.ActiveDocument.Paragraphs.Count; if (count > 10000) { return document.Content.Text; } string text = ""; for (int i = 0; i < count; i++) { string input = document.Paragraphs[i + 1].Range.Text.Replace("\f\r", ""); string pattern = "[\\r\\f\\n]"; string replacement = "\t"; string text2 = Regex.Replace(input, pattern, replacement); if (text2.EndsWith("\t")) { text2 = text2.Substring(0, text2.Length - 1); } text2 += "\r"; text += text2; } return text; } public string GetSelectedText() { // 文档对象 var document = Globals.ThisAddIn.Application.ActiveDocument; var vstoDocument = Globals.Factory.GetVstoObject(document); // 获取选区 var selection = Globals.ThisAddIn.Application.Selection; // 段落数 int count = selection.Paragraphs.Count; if (count > 10000) { return selection.Text; } string text = ""; if (count == 1) { string input = selection.Text.Replace("\f\r", ""); string pattern = "[\\r\\f\\n]"; string replacement = "\t"; string text2 = Regex.Replace(input, pattern, replacement); return text + text2; } for (int i = 0; i < count; i++) { string input; if (i == 0) { int start = selection.Range.Start; int end = selection.Paragraphs[i + 1].Range.End; Document obj = document; object Start = start; object End = end; input = obj.Range(ref Start, ref End).Text; } else if (i == count - 1) { int start2 = selection.Paragraphs[i + 1].Range.Start; int end2 = selection.Range.End; Document obj2 = document; object End = start2; object Start = end2; input = obj2.Range(ref End, ref Start).Text; } else { input = selection.Paragraphs[i + 1].Range.Text; } input = input.Replace("\f\r", ""); string pattern2 = "[\\r\\f\\n]"; string replacement2 = "\t"; string text3 = Regex.Replace(input, pattern2, replacement2); if (text3.EndsWith("\t")) { text3 = text3.Substring(0, text3.Length - 1); } text3 += "\r"; text += text3; } return text; } public void ShowLoginForm(string action) { Globals.ThisAddIn.ShowLoginForm(action); } public void ShowSettingForm() { FormSetting frm = new FormSetting(); frm.Show(); } public void MoveCursor(int pos) { var rng = Globals.ThisAddIn.Application.ActiveDocument.Range(pos, pos); rng.Select(); } public void SelectMarkById(int proofreadId, int documentId) { var doc = documentId < 1 ? Globals.ThisAddIn.Application.ActiveDocument : GetDocumentById(documentId); // // 设置当前文档数据 Globals.ThisAddIn.ActiveCurrentDocumentMarks(doc); if (proofreadId == selectProofreadId) return; // 取消上一个标签移除 if (selectProofreadId > 0 && marks.ContainsKey(selectProofreadId)) { var m = marks[selectProofreadId]; if (doc.Bookmarks.Exists(m.Name)) { marks[selectProofreadId].UnSelect(); } else { marks.Remove(selectProofreadId); } } selectProofreadId = proofreadId; if (proofreadId > 0 && marks.ContainsKey(proofreadId)) { var mark = marks[proofreadId].mark; // 已经不存在该标签了 if (!doc.Bookmarks.Exists(mark.Name)) { marks.Remove(selectProofreadId); return; } //object lineNum = (int)mark.Range.Information[WdInformation.wdFirstCharacterLineNumber] - 1; //object goToLine = WdGoToItem.wdGoToLine; //object goNext = WdGoToDirection.wdGoToNext; //Globals.ThisAddIn.Application.ActiveWindow.Selection.GoTo(ref goToLine, ref goNext, ref lineNum); // object bookmark = WdGoToItem.wdGoToBookmark; object bookmarkName = mark.Name; //doc.GoTo(mark); //mark.Range.GoTo(); Globals.ThisAddIn.Application.ActiveWindow.Selection.GoTo(ref bookmark, ref missing, ref missing, ref bookmarkName); // //mark.DisableCharacterSpaceGrid = false; // 先滚动到可视区域 //doc.ActiveWindow.ScrollIntoView(mark.Range); marks[proofreadId].Select(); //Globals.ThisAddIn.SendMessageToWeb("select", proofreadId); } Globals.ThisAddIn.SendMessageToWeb("select-proofread", proofreadId); } public void processMark(int proofreadId, int status) { if (proofreadId > 0 && marks.ContainsKey(proofreadId)) { marks[proofreadId].Process(status); } } private Document _currentDocument; public Document CurrentDocument { get => _currentDocument ?? Globals.ThisAddIn.Application.ActiveDocument; set { _currentDocument = value; } } public string InitContent(string content, int documentId) { try { // 根据文档编号 获取当前文档避免数据混乱 CurrentDocument = GetDocumentById(documentId); if (CurrentDocument == null) { throw new Exception("没有找到校对文档对象"); } List list = JsonConvert.DeserializeObject>(content); // 先清除所有数据 clearAllProofreadMark(CurrentDocument); //var app = Globals.ThisAddIn.Application; //var cur = app.Selection; // var prevOffset = 0; List disabledList = new List(); foreach (var correct in list) { if (correct.Diffs != null && correct.Diffs.Count > 0) { prevOffset = 0; int index = 0; foreach (var item in correct.Diffs) { int _prev = prevOffset; // 查找对应区域并再该区域添加书签 var mark = DocumentUtil.FindRangeAndCreateBookmark(item, correct, CurrentDocument, ref prevOffset); // 防止调用方法中没有更新 if (_prev >= prevOffset) { prevOffset = correct.SentenceOffset + item.start; } if (item.tag != "i") index++; if (mark != null) { marks.Add(item.id, new ProofreadItem(item, mark, documentId)); } else { disabledList.Add(item.id); var msg = new Dictionary{ {"message","没有找到标记对象" }, { "origin",item }, { "origin_correct",correct }, { "new_text",correct.NewText }, { "paragraph_num",correct.ParagraphNumber }, }; Logger.Log(JsonConvert.SerializeObject(msg)); } } } } foreach (var item in marks) { if (item.Value.mark != null) { if (item.Value.content.tag == "i") { item.Value.mark.Text = ToolUtil.GetBlankText(item.Value.content.text.Length); } if (item.Value.content.color != null) { try { var color = (WdColor)ColorTranslator.ToOle(Colors.FromHex(item.Value.content.color)); // 给选区添加背景颜色 item.Value.mark.Shading.BackgroundPatternColor = color; } catch (Exception) { //item.Value.mark.Shading.BackgroundPatternColor = WdColor.wdColorLightOrange; } } } } // 隐藏面板对应校对项 MainPanelWebMessage.DisabledProofreadItem(disabledList); } catch (Exception ex) { Logger.Log("Initial Content error:" + ex.Message + "\n" + ex.StackTrace + "\n\n"); return "false"; } return "true"; } void SendMessageToPanel() { } public string GetParagraph(int index) { try { var paragraphs = CurrentDocument.Paragraphs; var total = paragraphs.Count; // 判断索引是否超出范围 if (index > total) { return null; } var paragraph = CurrentDocument.Paragraphs[index]; return paragraph.Range.Text; } catch (Exception ex) { return "error:" + ex.Message; } } /// /// 新增校对项 查找时的偏移量 /// private static readonly int INSERT_FIND_OFFSET = 5; /// /// 根据校对项查找文档对应的位置 并创建书签 /// /// 校对项 /// 校对 /// /// public Microsoft.Office.Tools.Word.Bookmark FindRangeAndCreateBookmark1(CorrectedContent correct, DocumentCorrectItem sentense, ref int prevOffset) { Microsoft.Office.Tools.Word.Bookmark bookmark = null; try { var document = CurrentDocument; ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls; var markName = Config.BuildBookmarkName(correct.id); // 判断是否已经存在 if (controls.Contains(markName)) { try { controls.Remove(markName); } catch (Exception) { } } Range findRange = null; // 判断段落是否存在 if (sentense.ParagraphNumber > document.Paragraphs.Count) return null; var paragraph = document.Paragraphs[sentense.ParagraphNumber]; Range paragraphRange = paragraph.Range; var paragraphStart = paragraphRange.Start; var Start = paragraphStart + sentense.SentenceOffset; var End = Start + sentense.InsertLength - 1; if (End > paragraphRange.End) { End = paragraphRange.End; } // 当前句子的选区 var fullRange = document.Range(Start, End); // 如果选择有文本就用文本 string fullText = fullRange.Text ?? paragraphRange.Text; // 当前段落文本 string paragraphText = paragraphRange.Text; End = Start + correct.end; Start = Start + correct.start; // 避免越界 prevOffset = Math.Min(prevOffset, paragraphText.Length - 1); int offset = sentense.SentenceOffset; int length = sentense.InsertLength; string originText = sentense.Insert; // 如果是新增 则查找定位 if (correct.tag == "i") { // s1. 通过接口的位置 和 文档内容进行比对 try { // 判断接口的定位字符串和文档截取的字符串是否一致 if (paragraphText.Substring(offset, length) == originText) { // 直接使用接口定位 findRange = document.Range(Start, End); } } catch (Exception exif) { Logger.Log(exif); } // s2. 位置不匹配 则采用搜索定位 if (findRange == null) { // 找前缀 var prefix = correct.start > 2 ? ( correct.start > INSERT_FIND_OFFSET ? originText.Substring(correct.start - INSERT_FIND_OFFSET, INSERT_FIND_OFFSET) : originText.Substring(0, correct.start) ) : null; // 找后缀 var suffix = prefix == null ? ( correct.end + INSERT_FIND_OFFSET < originText.Length ? originText.Substring(correct.start, INSERT_FIND_OFFSET) : originText.Substring(correct.start, originText.Length - correct.start) ) : null; // 偏移量 var start = prefix != null || suffix != null ? paragraphText.IndexOf(prefix ?? suffix, prevOffset) : -1; if (start != -1) { var findOffset = paragraphStart + start + (prefix != null ? prefix.Length : 0); findRange = document.Range(findOffset, findOffset); } } } else { findRange = document.Range(Start, End); } // 不是新增模式 且定位区域文本和原始文本不匹配 if (correct.tag != "i" && findRange.Text != correct.origin) { // 查找 findRange = DocumentUtil.FindRange(correct, sentense, ref prevOffset, CurrentDocument, paragraphRange); } // 能够找到对应的区域 则再对应区域添加书签 if (findRange != null) { // 更新查找的结束位置 prevOffset = findRange.End - paragraphStart; bookmark = controls.AddBookmark(findRange, markName); bookmark.Tag = "ai_proofread"; } #region start //// 判断并获取当前段落 //// 定位要操作的文字 //var r = document.Range(Start, End); //// 判断选区是否正确 //if (item.tag == "i" || r.Text == item.origin) //{ // bookmark = controls.AddBookmark(r, markName); // bookmark.Tag = "ai_proofread"; //} //else //{ // object str = item.origin; // FindRange(ref fullRange, ref str); // if(fullRange != null){ // bookmark = controls.AddBookmark(fullRange, markName); // bookmark.Tag = "ai_proofread"; // } // //int startPos = 0, index, findCount = 0; // //while ((index = fullText.IndexOf(item.origin, startPos)) != -1) // //{ // // if (findCount == findIndex) // // { // // r = document.Range(offset + index, offset + index + item.origin.Length); // // if(r.Text == item.origin) { } // // bookmark = controls.AddBookmark(r, markName); // // bookmark.Tagetg = "ai_proofread"; // // break; // // } // // startPos = index; // // findCount++; // //} //} #endregion } catch (Exception ex) { Logger.Log("create mark error:" + ex.Message + "\n" + ex.StackTrace + "\n\n"); } return bookmark; } /// /// /// /// /// private void FindRange(ref Range range, ref object whatToFind) { object matchCase = false; // 是否区分大小写 object matchWholeWord = false; // 是否匹配整个单词 object matchWildcards = false; // 是否使用通配符 object forward = true; // 搜索方向,true表示向前搜索,false表示向后搜索 object wrap = Microsoft.Office.Interop.Word.WdFindWrap.wdFindContinue; // 搜索范围,这里表示继续搜索 range.Find.Execute(ref whatToFind, ref matchCase, ref matchWholeWord, ref matchWildcards, ref forward, ref wrap); } } }