Word 中的公式解析和还原方式与说明

时间:2024-03-17 17:51:31

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,...