//using Microsoft.Office.Interop.Word; using System.Drawing; using UtilLib; using System.Collections.Generic; using System; using Newtonsoft.Json; using Microsoft.Office.Interop.Word; using Microsoft.Office.Tools.Word; using Bookmark = Microsoft.Office.Tools.Word.Bookmark; using Section = Microsoft.Office.Interop.Word.Section; using WdColor = Microsoft.Office.Interop.Word.WdColor; using NPOI.XSSF.UserModel; using System.Windows.Forms; using NPOI.SS.UserModel; using System.IO; namespace AIProofread { public class DocumentUtil { static int markId = 0; /// /// 添加一个书签 /// /// /// /// /// public static Bookmark AddBookmark(string color, int start, int end) { var doc = Globals.ThisAddIn.Application.ActiveDocument; var maxOffset = doc.Range().End; if (start > maxOffset || end > maxOffset) { return null; } var document = Globals.Factory.GetVstoObject(doc); var r = document.Range(start, end); var mark = document.Controls.AddBookmark(r, string.Format("ai_mark_{0}", markId++)); mark.Tag = "ai_proofread"; if (color != null) { // 给选区添加背景颜色 r.Shading.BackgroundPatternColor = (WdColor)ColorTranslator.ToOle(Colors.FromHex(color)); } return mark; } public static void SectionAddMark(string color) { var document = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument); // 获取当前文档对象 var doc = Globals.ThisAddIn.Application.ActiveDocument; // 获取选中 var sections = document.Sections; foreach (Section section in sections) { var r = section.Range; //Bookmark mark = r.Bookmarks.Add(); var mark = document.Controls.AddBookmark(r, string.Format("ai_mark_{0}", markId++)); mark.Tag = "ai_proofread"; // 给选区添加背景颜色 r.Shading.BackgroundPatternColor = (WdColor)ColorTranslator.ToOle(Colors.FromHex(color)); } } public static System.Collections.Generic.List GetAllBookmark() { var bookmarks = Globals.ThisAddIn.Application.ActiveDocument.Bookmarks; System.Collections.Generic.List list = new System.Collections.Generic.List(); foreach (Microsoft.Office.Interop.Word.Bookmark mark in bookmarks) { list.Add(mark.Name); } return list; } /// /// 删除所有标签 /// public static void RemoveBookmark() { RemoveBookmark(null); } public static void ClearProofreadMarks() { var document = Globals.ThisAddIn.Application.ActiveDocument; var bookmarks = document.Bookmarks; ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls; foreach (Microsoft.Office.Interop.Word.Bookmark mark in bookmarks) { if (Config.IsProofreadMark(mark.Name)) { // 去除高亮 mark.Range.Shading.BackgroundPatternColor = WdColor.wdColorAutomatic; // 去除下划线 //mark.Range.Underline = WdUnderline.wdUnderlineNone; try { mark.Delete(); } catch (Exception e) { Logger.Log("remove mark", e); } } } } /// /// 删除标签 /// /// public static void RemoveBookmark(string markId) { var document = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument); if (document.Bookmarks.Count == 0) return; // 不存在要移除的标签 if (null != markId && !document.Bookmarks.Exists(markId)) return; var bookmarks = Globals.ThisAddIn.Application.ActiveDocument.Bookmarks; foreach (Microsoft.Office.Interop.Word.Bookmark mark in bookmarks) { if (mark.Name == markId) { mark.Range.Shading.BackgroundPatternColor = (WdColor)WdColorIndex.wdAuto; mark.Delete(); } } //foreach (var mark in document.Bookmarks) //{ // if (markId != null) // { // if (mark.Name == markId) // { // // 删除 // mark.Delete(); // mark.Range.Shading.BackgroundPatternColor = (WdColor)WdColorIndex.wdAuto; // return; // } // } // else // { // mark.Delete(); // } //} } public static System.Collections.Generic.List GetSectionText() { var document = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument); System.Collections.Generic.List list = new System.Collections.Generic.List(); if (document.Sections.Count == 0) return list; foreach (Section item in document.Sections) { list.Add(item.Range.Text); } return list; } /// /// 查找偏移量 /// private static readonly int INSERT_FIND_OFFSET = 5; /// /// 查找校对项对应的range /// /// 校对项 /// 当前校对项所在句子 /// 上一个校对项结束位置 /// /// /// public static Range FindRange(CorrectItem correct, CorrectContext sentense, ref int prevOffset, Microsoft.Office.Interop.Word.Document document, Range range) { var paragraphText = range.Text; string findText = correct.Origin; int wordStart = correct.Start; int wordEnd = correct.End; int offset = sentense.SentenceOffset; string originText = sentense.Insert; object Start = range.Start + offset + wordStart; object End = range.Start + offset + wordEnd; try { var activeDocument = document; // 查找对象位置 End = range.Start + offset + wordEnd; Start = range.Start + offset + wordStart; // 直接找到 var findRange = activeDocument.Range(ref Start, ref End); // 判断对应选区是否是要找的文本 if (findRange.Text == findText) { return findRange; } #region 使用前后关联进行强匹配 // 找前缀 var prefix = wordStart > 2 ? ( wordStart > INSERT_FIND_OFFSET ? originText.Substring(wordStart - INSERT_FIND_OFFSET, INSERT_FIND_OFFSET) : originText.Substring(0, wordStart) ) : null; // 找后缀 var suffix = prefix == null ? ( wordEnd + INSERT_FIND_OFFSET < originText.Length ? originText.Substring(wordStart, INSERT_FIND_OFFSET) : originText.Substring(wordStart, originText.Length - wordStart) ) : null; var start = prefix != null || suffix != null ? paragraphText.IndexOf(prefix ?? suffix, prevOffset) // item.start + : -1; if (start != -1) { var findOffset = range.Start + start + (prefix != null ? prefix.Length : 0); findRange = document.Range(findOffset, findOffset + wordEnd - wordStart); if (findRange.Text == findText) { return findRange; } } #endregion // 使用段落字符串进行强匹配 start = paragraphText.IndexOf(findText, prevOffset); if (start != -1) { start += range.Start; return document.Range(start, start + findText.Length); } var msg1 = new Dictionary{ {"message",range.Find.Found?"搜索到可用位置":"没有搜索到可用位置" }, { "search_start",range.Start }, { "search_end",range.End }, { "search_text",range.Text } }; Logger.Log(JsonConvert.SerializeObject(msg1)); //while (range.Find.Found) //{ // var obj4 = range.Document; // MatchControl = range.Start; // MatchAlefHamza = range.End; // var range2 = obj4.Range(ref MatchControl, ref MatchAlefHamza); // num3 = range2.End; // if (range2.Text == findText) // { // return range2; // } // break; // //if (findIndex == num) // //{ // //} // //num++; // //range.Find.MatchByte = true; // //Find find2 = range.Find; // //MatchAlefHamza = missword; // //MatchControl = true; // //MatchDiacritics = Type.Missing; // //MatchKashida = Type.Missing; // //Replace = Type.Missing; // //ReplaceWith = Type.Missing; // //Format = Type.Missing; // //Wrap = Type.Missing; // //Forward = Type.Missing; // //MatchAllWordForms = Type.Missing; // //MatchSoundsLike = Type.Missing; // //MatchWildcards = Type.Missing; // //MatchWholeWord = Type.Missing; // //Start = Type.Missing; // //End = Type.Missing; // //// 再次重复查找 // //find2.Execute(ref MatchAlefHamza, ref MatchControl, ref MatchDiacritics, ref MatchKashida, ref Replace, ref ReplaceWith, ref Format, ref Wrap, ref Forward, ref MatchAllWordForms, ref MatchSoundsLike, ref MatchWildcards, ref MatchWholeWord, ref Start, ref End); //} //if (num3 == 0) //{ // return null; //} } catch (Exception ex) { Logger.Log(ex); } return null; } public static Bookmark FindBookMarkByCorrect(CorrectItem correct) { try { var document = Globals.ThisAddIn.ActiveDocument.CurrentDocument; var marks = document.Bookmarks; var markName = Config.BuildBookmarkName(correct.Id); if (!document.Bookmarks.Exists(markName)) return null; ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls; //return controls[markName] as Bookmark; //var obj = controls[markName]; var bookmark = marks[markName]; var start = bookmark.Range.Start; var end = bookmark.Range.End; // 删除原有书签 controls.Remove(markName); return controls.AddBookmark(document.Range(start, end), markName); } catch (Exception ex) { Logger.Log(ex); } return null; //if(bookmark == null) //{ // foreach (var m in marks) // { // var mark = m as Bookmark; // var name = mark.Name; // var tag = mark.Tag?.ToString(); // if(tag == markName || name == markName) // { // return mark; // } // } //} //return bookmark as Bookmark; } public static Bookmark FindRangeAndCreateBookmark(CorrectItem correct, CorrectContext sentense, Microsoft.Office.Interop.Word.Document document, ref int prevOffset) { Bookmark bookmark = null; try { ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls; var markName = Config.BuildBookmarkName(correct.Id); // 判断是否已经存在 if (controls.Contains(markName)) { try { controls.Remove(markName); } catch (Exception) { } } // 判断段落是否存在 if (sentense.ParagraphNumber > document.Paragraphs.Count) return null; var paragraph = document.Paragraphs[sentense.ParagraphNumber]; var findRange = FindRangeByCorrect(sentense, correct, paragraph, document, prevOffset); if (findRange != null) { // 更新查找的结束位置 //prevOffset = findRange.End - paragraphStart; bookmark = controls.AddBookmark(findRange, markName); bookmark.Tag = "ai_proofread"; } } catch (Exception ex) { Logger.Log("create mark error:" + ex.Message + "\n" + ex.StackTrace + "\n\n"); } return bookmark; } private static Range FindRangeByCorrect(CorrectContext c, CorrectItem item, Paragraph paragraph, Microsoft.Office.Interop.Word.Document document, int prevOffset) { var paraRange = paragraph.Range; var paraText = paraRange.Text; var paraStart = paraRange.Start; // 定位句子的其实位置 //var offset = paraStart + c.SentenceOffset; ////var cutLength = Math.Min(c.InsertLen, paraText.Length - offset); /// TODO 目前接口没有返回 句子相关数据 直接获取 var sentence = paraRange.Sentences[c.SentenceNumber]; //paraText.Substring(c.SentenceOffset, c.InsertLength); c.SentenceOffset = sentence.Start; var offset = c.SentenceOffset; c.Insert = sentence.Text; if (sentence.Text == c.Insert) { if (item.Tag == "i") { return document.Range(offset + item.Start, offset + item.Start); } // 比对原始内容与校对原文是否一致 var range = document.Range(offset + item.Start, offset + item.End + 1); // if (range.Text == item.Origin) return range; } var originText = c.Insert; // 如果是新增 则查找定位 if (item.Tag == "i") { // 找前缀 var prefix1 = item.Start > 2 ? ( item.Start > INSERT_FIND_OFFSET ? originText.Substring(item.Start - INSERT_FIND_OFFSET, INSERT_FIND_OFFSET) : originText.Substring(0, item.Start) ) : null; // 找后缀 var suffix1 = prefix1 == null ? ( item.End + INSERT_FIND_OFFSET < originText.Length ? originText.Substring(item.Start, INSERT_FIND_OFFSET) : originText.Substring(item.Start, originText.Length - item.Start) ) : null; // 偏移量 var start1 = prefix1 != null || suffix1 != null ? paraText.IndexOf(prefix1 ?? suffix1, prevOffset) : -1; if (start1 != -1) { var findOffset = paraStart + start1 + (prefix1 != null ? prefix1.Length : 0); return document.Range(findOffset, findOffset + 1); } } // 执行查找 int wordStart = item.Start; int wordEnd = item.End; // 找前缀 var prefix = wordStart > 2 ? ( wordStart > INSERT_FIND_OFFSET ? originText.Substring(wordStart - INSERT_FIND_OFFSET, INSERT_FIND_OFFSET) : originText.Substring(0, wordStart) ) : null; // 找后缀 var suffix = prefix == null ? ( wordEnd + INSERT_FIND_OFFSET < originText.Length ? originText.Substring(wordStart, INSERT_FIND_OFFSET) : originText.Substring(wordStart, originText.Length - wordStart) ) : null; var start = prefix != null || suffix != null ? paraText.IndexOf(prefix ?? suffix, prevOffset) // item.start + : -1; if (start != -1) { var findOffset = paraRange.Start + start + (prefix != null ? prefix.Length : 0); var range = document.Range(findOffset, findOffset + wordEnd - wordStart + 1); if (range.Text == item.Origin) { return range; } } // 直接定位查找 start = paraText.IndexOf(item.Origin, prevOffset); if (start == -1) return null; // 定位整体开始位置 start = paraStart + start; return document.Range(start, start + item.Origin.Length); } public static void ExportProofreadResult() { SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "Excel文件|*.xlsx"; var result = sfd.ShowDialog(); // 如果用户取消选择,则返回 if (result == DialogResult.Cancel) { return; } using (var fs = File.OpenWrite(sfd.FileName)) { var book = new XSSFWorkbook(); var sheet = book.CreateSheet("Sheet1"); // 设置表格样式 var style = CreateBaseCellStyle(book); style.SetFont(CreateBaseFont(book)); style.BorderBottom = NPOI.SS.UserModel.BorderStyle.None; style.BorderTop = NPOI.SS.UserModel.BorderStyle.None; style.BorderLeft = NPOI.SS.UserModel.BorderStyle.None; style.BorderRight = NPOI.SS.UserModel.BorderStyle.None; style.WrapText = true; sheet.SetDefaultColumnStyle(0, style); sheet.SetDefaultColumnStyle(1, style); sheet.SetDefaultColumnStyle(2, style); sheet.SetDefaultColumnStyle(3, style); sheet.SetDefaultColumnStyle(4, style); sheet.SetDefaultColumnStyle(5, style); sheet.SetDefaultColumnStyle(6, style); // 表头设置 var row = sheet.CreateRow(0); row.CreateCell(0).SetCellValue("序号"); row.CreateCell(1).SetCellValue("页"); row.CreateCell(2).SetCellValue("行"); var cell = row.CreateCell(3);// // 设置宽度 sheet.SetColumnWidth(3, 50 * 256); cell.SetCellValue("详细信息"); var cellExp = row.CreateCell(4); // 设置文字颜色为红色 var expStyle = book.CreateCellStyle(); var f1 = CreateBaseFont(book); f1.Color = NPOI.HSSF.Util.HSSFColor.Red.Index; expStyle.SetFont(f1); cellExp.CellStyle = expStyle; cellExp.SetCellValue("异常"); var suggestCell = row.CreateCell(5);// suggestCell.CellStyle.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Red.Index; suggestCell.SetCellValue("建议"); row.CreateCell(6).SetCellValue("处理状态"); var blackFont = CreateBaseFont(book); blackFont.Color = NPOI.HSSF.Util.HSSFColor.Black.Index; var redFont = CreateBaseFont(book); redFont.Color = NPOI.HSSF.Util.HSSFColor.Red.Index; int id = 1; foreach (var item in Globals.ThisAddIn.ActiveDocument.marks) { if (item.Value.mark == null) continue; var it = item.Value.content; var range = item.Value.mark.Range; // 获取书签在文档的页码数 var pageNumber = range.get_Information(WdInformation.wdActiveEndPageNumber); // 获取书签在当前页面的行数 var lineNumber = range.get_Information(WdInformation.wdFirstCharacterLineNumber); row = sheet.CreateRow(id); row.CreateCell(0).SetCellValue(id); row.CreateCell(1).SetCellValue(pageNumber); row.CreateCell(2).SetCellValue(lineNumber); XSSFRichTextString originText = new XSSFRichTextString(item.Value.OriginSentence); originText.ApplyFont(blackFont); var startIndex = item.Value.OriginSentence.IndexOf(it.Origin); // 对查找内容引用红色 originText.ApplyFont(startIndex, startIndex + it.Origin.Length, redFont); row.CreateCell(3).SetCellValue(originText); row.CreateCell(4).SetCellValue(it.Origin); if(it.Tag == "r") { var suggest = it.Text; if(it.Type == "sensitive") { suggest += "(敏感词)"; } else if (it.Type == "blacklist") { suggest += "(敏感词)"; } else if (!string.IsNullOrEmpty(it.Addition)) { suggest += $"({it.Addition})"; } row.CreateCell(5).SetCellValue(suggest); } else if(it.Tag == "i") { row.CreateCell(5).SetCellValue("新增"); } else if (it.Tag == "e") { row.CreateCell(5).SetCellValue("删除"); } row.CreateCell(6).SetCellValue(StatusText(it.IsAccept)); id++; } // 保存到文件 book.Write(fs); } } private static ICellStyle CreateBaseCellStyle(IWorkbook workbook) { var style = workbook.CreateCellStyle(); style.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; style.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center; style.WrapText = true; return style; } private static IFont CreateBaseFont(IWorkbook workbook) { // 宋体 11 var font = workbook.CreateFont(); font.FontName = "宋体"; font.FontHeightInPoints = 11; return font; } private static string StatusText(int status) { if (status == AcceptStatus.Accept) return "采纳"; else if (status == AcceptStatus.Review) return "复核"; else if (status == AcceptStatus.Ignore) return "忽略"; return "未处理"; } } }