2024-08-17 10:09:31 +08:00

761 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AIProofread.Controls;
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.Threading.Tasks;
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<string, WebView2> webViewDict = new Dictionary<string, WebView2>();
private static readonly Dictionary<BridgeEvent, List<BridgeEventHandler>> eventHandlers = new Dictionary<BridgeEvent, List<BridgeEventHandler>>();
public static Dictionary<int, ProofreadItem> marks;
private static int selectProofreadId = -1;
private static object missing = System.Reflection.Missing.Value;
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();
}
/// <summary>
/// 添加事件
/// </summary>
/// <param name="eventName"></param>
/// <param name="handler"></param>
public static void AddEventHandler(BridgeEvent eventName, BridgeEventHandler handler)
{
if (!eventHandlers.ContainsKey(eventName))
{
eventHandlers.Add(eventName, new List<BridgeEventHandler>());
}
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)
{
var doc = Globals.ThisAddIn.Application.ActiveDocument;
Globals.ThisAddIn.documentIdDics[doc] = id;
}
public int GetCurrentDocumentId()
{
var doc = Globals.ThisAddIn.Application.ActiveDocument;
var dics = Globals.ThisAddIn.documentIdDics;
return dics.ContainsKey(doc) ? dics[doc] : 0;
}
// 打开网页
public void OpenUrlWithOsBrowser(string url)
{
try
{
Process.Start(url);
}
catch
{
showDialog("打开网页失败");
}
}
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);
}
}
/// <summary>
/// 登录成功后通知后端
/// </summary>
public void loginSuccess(string userinfo)
{
if (userinfo == null || userinfo.Length == 0) return;
//
Userinfo info = JsonConvert.DeserializeObject<Userinfo>(userinfo);
// 登录成功 展示
Globals.ThisAddIn.ribbon.ProcessLoginInfo(info);
//Globals.ThisAddIn.SendMessageToWeb("async-login-success", null);
}
// 获取文档所有文本数据
public Dictionary<string, object> getAllText()
{
return Tools.GetAllText(Globals.ThisAddIn.Application.ActiveDocument);
}
public string getDocumentData()
{
Dictionary<string, object> data = new Dictionary<string, object>();
var doc = Globals.ThisAddIn.Application.ActiveDocument;
data.Add("name", doc.Name);
data.Add("fullName", doc.FullName);
data.Add("wordsCount", doc.Words.Count);
data.Add("charactersCount", doc.Characters.Count);
Application app = new Application();
object missing = System.Reflection.Missing.Value;
object saveOption = WdSaveOptions.wdDoNotSaveChanges;
// 创建临时文件
string path = CreateTempDocumentFile(doc);
Document tmpDoc = app.Documents.Open(path, false, true, false);
data.Add("content", Tools.GetAllText(tmpDoc));
// 关闭并释放对象
tmpDoc.Close(ref saveOption, ref missing, ref missing);
Marshal.ReleaseComObject(tmpDoc);
app.Quit(ref saveOption,ref missing,ref missing);
Marshal.ReleaseComObject(app);
// 显式回收一下吧
GC.Collect();
File.Delete(path);
return Tools.GetJSONString(data);
}
private string CreateTempDocumentFile(Document doc)
{
// 先转成临时文件
FileStream fs = new FileStream(doc.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//
var tmpFile = Path.GetTempFileName();
byte[] bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
FileStream tmp = new FileStream(tmpFile, FileMode.OpenOrCreate,FileAccess.ReadWrite);
tmp.Write(bytes, 0, bytes.Length);
// 关闭吧
tmp.Close();
fs.Close();
return tmpFile;
}
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<string> list = new List<string>();
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<string, object> data = new Dictionary<string, object>
{
{"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;
}
/// <summary>
/// 读取文档原始文件并转换成base64
/// </summary>
/// <returns></returns>
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<UpgradeData>(data);
var ret = System.Windows.Forms.MessageBox.Show(upgradeData.Message, "更新提示", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question);
if (ret == System.Windows.Forms.DialogResult.Yes)
{
OpenUrlWithOsBrowser(upgradeData.DownloadUrl);
}
}
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)
{
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);
}
// 清除所有标记
public void clearAllProofreadMark()
{
// 设置当前文档数据
Globals.ThisAddIn.ActiveCurrentDocumentMarks();
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<CorrectedContent>(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)
{
// 设置当前文档数据
Globals.ThisAddIn.ActiveCurrentDocumentMarks();
if (proofreadId == selectProofreadId) return;
var doc = Globals.ThisAddIn.Application.ActiveDocument;
// 取消上一个标签移除
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);
}
}
public void InitContent(string content)
{
try
{
List<DocumentCorrectItem> list = JsonConvert.DeserializeObject<List<DocumentCorrectItem>>(content);
// 先清除所有数据
clearAllProofreadMark();
//var app = Globals.ThisAddIn.Application;
//var cur = app.Selection;
//
foreach (var correct in list)
{
//cur.TypeText(correct.Insert);
//var rng = cur.Range;
if (correct.Diffs != null && correct.Diffs.Count > 0)
{
//var diffs = correct.Diffs.OrderBy(it =>
//{
// if (it.tag != "i") return it.start;
// return it.start + correct.Insert_len;
//}).ToList();
int index = 0;
foreach (var item in correct.Diffs)
{
if (item.idx == 3330)
{
Console.WriteLine("xx");
}
var mark = AddBookmark(item, index, correct.Sentence_offset, correct.Insert_len, correct.Paragraph_num);
if (item.tag != "i") index++;
if (mark != null)
{
marks.Add(item.id, new ProofreadItem(item, mark));
}
else
{
var msg = new Dictionary<string, object>{
{"message","没有找到标记对象" },
{ "origin",item },
{ "origin_correct",correct },
{ "new_text",correct.New_text },
{ "paragraph_num",correct.Paragraph_num },
};
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)
{
// 给选区添加背景颜色
item.Value.mark.Shading.BackgroundPatternColor = (WdColor)ColorTranslator.ToOle(Colors.FromHex(item.Value.content.color));
}
}
}
}
catch (Exception ex)
{
Logger.Log("Initial Content error:" + ex.Message + "\n" + ex.StackTrace + "\n\n");
}
}
public string GetParagraph(int index)
{
var document = Globals.ThisAddIn.Application.ActiveDocument;
try
{
var paragraphs = document.Paragraphs;
var total = paragraphs.Count;
// 判断索引是否超出范围
if (index > total)
{
return null;
}
var paragraph = document.Paragraphs[index];
return paragraph.Range.Text;
}
catch (Exception ex)
{
return "error:" + ex.Message;
}
}
public Microsoft.Office.Tools.Word.Bookmark AddBookmark(CorrectedContent item, int findIndex, int offset, int length, int paragraphIndex)
{
Microsoft.Office.Tools.Word.Bookmark bookmark = null;
try
{
var document = Globals.ThisAddIn.Application.ActiveDocument;
ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls;
var markName = Config.BuildBookmarkName(item.id);
// 判断是否已经存在
if (controls.Contains(markName))
{
try
{
controls.Remove(markName);
}
catch (Exception) { }
}
Range findRange = null;
if (paragraphIndex > document.Paragraphs.Count) return null;
var paragraph = document.Paragraphs[paragraphIndex];
var paragraphStart = paragraph.Range.Start;
offset += paragraphStart;
var Start = offset;
var End = Start + length - 1;
if (End > paragraph.Range.End)
{
End = paragraph.Range.End;
}
// 当前句子的选区
var fullRange = document.Range(Start, End);
string fullText = fullRange.Text ?? paragraph.Range.Text;
Start = offset + item.start;
End = offset + item.end;
// 如果是新增 只能使用接口数据
findRange = document.Range(Start, End);
if (item.tag != "i" && findRange.Text != item.origin)
{
findRange = DocumentUtil.FindRange(paragraphIndex, item.origin, item.start, item.end);
}
if (findRange != null)
{
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);
}
}
}