Word 中的公式解析和还原方式与说明
Word 中的公式解析和还原方式与说明
首先我们知道doc(office 2003)是流,docx(> office 2007)是open xml格式
我想跨平台解析wrod 里面的公式 docx(> office 2007),于是我发现公式有3种格式(可能>3种)
这里有一个差异化文档信息(Vsto和OpenXml对比):
https://kdocs.cn/l/seF0uZqMdfVc
目前Html里常用公式种类有:LaTex,MathML(点击手写生成公式)
目前Word里常用公式种类有:EQ公式域(eq field),OMath公式(omml),MathType公式,axMath公式(国产)
//附:域获取方法(如需转为图片请看文章 https://www.cnblogs.com/ping9719/p/15205267.html) Application.ActiveDocument.Fields.Item(1).Code.Text
1.OMath公式(office>=2007)(只有这种支持open xml解析,通过OMML2MML.XSL文件;eq理论上也支持,但是需要自己写)
2.老公式(office<2007)
3.wps老公式
于是我写下了 这一篇文章 把 word 解析为html 希望支持 文字,图片,公式(未实现),表格(未实现)
https://www.cnblogs.com/ping9719/p/12462478.html (如果需要简单的解析为html的可以参考此文档)
根据上面的文章,我并不想管其他2个公式(只支持“1.新ms office公式(>=2007)”这种方法),我发现也有很多的问题:
1.公式解析是别人写的
2.公式前端还需要额外的js库
3.公式没有办法逆转回去(html->word),除非自己写逆转方法(太难了)。
于是我在换思路:
所以我这边建议使用 wps 或者 ms office 转为html+xml,这样子就支持了所有的公式和格式
wps 支持跨平台(jsapi):
https://code.aliyun.com/zouyingfeng/wps/tree/master
https://zhuanlan.zhihu.com/c_1256350603921915904
但是为了方便我没有使用wps+jsapi
我使用为wps+vsto,他们的类库差不多的,可以有参考价值
实现的思路为:
代码实现:
建立项目 net standard 2.0
随便为了支持一下 net 4.5版本
编辑 .csproj 文件
<PropertyGroup> <TargetFrameworks>net45;netstandard20</TargetFrameworks> </PropertyGroup>
引用COM:
引用包对C#语言支持:
我的项目结构为:
这个结构方便我以后对ppt和excel的支持代码,同事支持wps+ms office
下面开始:
加入文件 OfficeApp.cs
/// <summary> /// office应用 /// </summary> public class OfficeApp { /// <summary> /// WPS 2019+窗体模式 是否为整合模式 /// </summary> private bool? IsWpsFormMergeModel { get { return null; } } /// <summary> /// 得到进程中的word应用 /// </summary> /// <param name="appType">0没有找到 1 office 2 wps</param> /// <returns>word应用</returns> public static object GetProcessesWordApp(out OfficeType officeType) { officeType = OfficeType.Null; //MS Office if (Process.GetProcessesByName("WINWORD").Count() > 0) { officeType = OfficeType.Microsoft; return DllImportHelp.GetActiveObject("Word.Application"); } else if (Process.GetProcessesByName("wps").Count() > 0) { //WPS Office V9 if (Type.GetTypeFromProgID("KWPS.Application") != null) { officeType = OfficeType.WPS; return DllImportHelp.GetActiveObject("KWPS.Application"); } //WPS Office V8 else if (Type.GetTypeFromProgID("WPS.Application") != null) { officeType = OfficeType.WPS; return DllImportHelp.GetActiveObject("WPS.Application"); } else return null; } else { return null; } } }
加入文件 RangeEx.cs
public static class RangeEx { /// <summary> /// 统一设置颜色(文字,下划线,音调符号,表格边框) /// </summary> public static void SetColor(this Range range, WdColor wdColor) { range.Font.Color = wdColor; range.Font.DiacriticColor = wdColor; range.Font.UnderlineColor = wdColor; foreach (Table table in range.Tables) { table.Borders.InsideColor = wdColor; table.Borders.OutsideColor = wdColor; } } /// <summary> /// 是否无内容 /// </summary> public static bool IsNull(this Range range) { //对公式等检测 if (range.Text.Length == range.End - range.Start) { //对图片等检测 string txt = range.Text.Replace("\r", "").Replace("\n", "").Replace("\v", ""); if (string.IsNullOrEmpty(txt)) return true; } return false; } /// <summary> /// 是否无内容或者空格组成 /// </summary> public static bool IsNullOrSpace(this Range range) { //对公式等检测 if (range.Text.Length == range.End - range.Start) { //对图片等检测 string txt = range.Text.Trim(); if (string.IsNullOrWhiteSpace(txt)) return true; } return false; } /// <summary> /// 替换 /// </summary> /// <param name="range"></param> /// <param name="findText">查找内容</param> /// <param name="replacementText">替换内容</param> public static bool Replace(this Range range, string findText, string newValue) { Find find = range.Find; find.Replacement.ClearFormatting(); find.Text = findText; find.Replacement.Text = newValue; find.Forward = false; find.Wrap = WdFindWrap.wdFindStop; find.Format = false; find.MatchCase = true;//区分大小写 find.MatchWholeWord = false; find.MatchByte = true; find.MatchAllWordForms = false; find.MatchSoundsLike = false; find.MatchWildcards = false; find.MatchFuzzy = false; return find.Execute(MatchControl: WdReplace.wdReplaceAll); } /// <summary> /// 得到环绕模式为悬浮的表格 /// </summary> /// <param name="range"></param> public static List<Table> GetFloatTable(this Range range) { if (range == null) //....................此文件十分重要,只贴部分代码......
加入文件 DllImportHelp.cs
public static class DllImportHelp { /// <summary> /// net core 没有GetActiveObject()方法 所以调用dll /// </summary> /// <param name="progId"></param> /// <returns></returns> public static object GetActiveObject(string progId) { if (progId == null) return null; var hr = CLSIDFromProgIDEx(progId, out var clsid); if (hr < 0) return null; hr = GetActiveObject(clsid, IntPtr.Zero, out var obj); if (hr < 0) return null; return obj; } [DllImport("ole32")] private static extern int CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] string lpszProgID, out Guid lpclsid); [DllImport("oleaut32")] private static extern int GetActiveObject([MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppunk); }
/// <summary> /// office程序类型 /// </summary> public enum OfficeType { /// <summary> /// 为空的 /// </summary> Null, /// <summary> /// WPS Office /// </summary> WPS, /// <summary> /// Microsoft Office /// </summary> Microsoft, /// <summary> /// 未知的程序 /// </summary> Unknown, }
加入文件 WordApp.cs
/// <summary> /// Word应用 /// </summary> public class WordApp { /// <summary> /// 查找符:软回车,同html中的br /// </summary> public const string Find_BlankRow = "^l"; /// <summary> /// 查找符:段落 /// </summary> public const string Find_Paragraph = "^p"; /// <summary> /// 查找符:段落 通配符 /// </summary> public const string Find_ParagraphASCII = "^13"; private OfficeType _AppType = OfficeType.Null; /// <summary> /// Office类型 /// </summary> public OfficeType AppType { get { if (_AppType != OfficeType.Null) return _AppType; if (File.Exists(App.Path + "/wps.exe") || File.Exists(App.Path + "wpsoffice.exe")) _AppType = OfficeType.WPS; else if (File.Exists(App.Path + "/MSOHTMED.EXE") || File.Exists(App.Path + "MSQRY32.EXE")) _AppType = OfficeType.Microsoft; else _AppType = OfficeType.Unknown; return _AppType; } private set { _AppType = value; } } /// <summary> /// 整个应用程序 /// </summary> public Application App { get; private set; } /// <summary> /// 活动中(正在操作)的Word文档 /// </summary> public Document Doc { get; private set; } /// <summary> /// 是否存在文件路径 /// </summary> public bool IsExistFilePath { get => Doc.FullName.Contains(\':\'); } ///// <summary> ///// 初始化程序和活动中的word (vsto) ///// </summary> //public WordApp() //{ // App = Globals.ThisAddIn.Application; // try // { // Doc = App.ActiveDocument; // } // catch { } //} /// <summary> /// 初始化word程序 /// </summary> public WordApp() { var wordObj = OfficeApp.GetProcessesWordApp(out OfficeType officeType); if (wordObj == null) App = new Application(); else { App = (Application)wordObj; AppType = officeType; } Doc = App.Documents.Add(); } /// <summary> /// 初始化word程序 /// </summary> public WordApp(string fileName) { var wordObj = OfficeApp.GetProcessesWordApp(out OfficeType officeType); if (wordObj == null) App = new Application(); else { App = (Application)wordObj; AppType = officeType; } Doc = App.Documents.Open(fileName); } /// <summary> /// 初始化程序,需要有活动中的word /// </summary> public WordApp(Application app) { App = app; try { Doc = app.ActiveDocument; } catch { } } /// <summary> /// 初始化word程序 /// </summary> public WordApp(Document doc) { if (doc != null) { App = doc.Application; Doc = doc; } } /// <summary> /// 全新的文档 /// </summary> public void NewDoc() { Close(); Doc = App.Documents.Add(); } /// <summary> /// 得到文档的数量 /// </summary> public int GetDocCount() { if (App == null) return 0; return App.Documents.Count; } /// <summary> /// 替换(WPS 有些情况有bug) /// </summary> /// <param name="findText">查找内容</param> /// <param name="newValue">替换内容</param> public bool Replace(string findText, string newValue) { return RangeEx.Replace(Doc.Content, findText, newValue); }
//....................此文件十分重要,只贴部分代码......
--------------------------------------------------
-----------------------------------------------
-----------------------
-----------
---
开始(最重要的代码):
//导出 //全文 new WordApp().Doc.Content.ExportFragment(@"D:\123.html", WdSaveFormat.wdFormatHTML); //部分,段落,公式... Range.ExportFragment(@"D:\123.html", WdSaveFormat.wdFormatHTML); //全文 new WordApp().Doc.Content.ExportFragment(@"D:\123.xml", WdSaveFormat.wdFormatXMLDocument); //部分,段落,公式... Range.ExportFragment(@"D:\123.xml", WdSaveFormat.wdFormatXMLDocument);
html里面有很多无用的标签,需要自己清除
//还原 Range.InsertFile(@"D:\123.html"); Range.InsertFile(@"D:\123.xml");
部署到IIS的问题:
1.需要安装。net core 对应版本的运行时(自己百度)
2.需要给iis管理员权限(https://blog.csdn.net/q646926099/article/details/52421273)
ok,...