/// <summary>
        /// xml配置文件中添加的书签替换
        /// </summary>
        /// <param name="bookmarkstart"></param>
        /// <param name="bookMarkConfs"></param>
        /// <param name="ctx"></param>
        /// <param name="logger"></param>
        /// <param name="missMatch"></param>
        private bool UpdateBookMarksByCfg(OpenXmlBookmarkStart bookmarkstart, BookMarkConfs bookMarkConfs, ReplacerContext <T> ctx, Action <string> logger, Action <string> missMatch)
        {
            if (null == bookMarkConfs || bookMarkConfs.Confs.Count < 1)
            {
                logger?.Invoke("配置文件:宏替换器内容为空");
                return(false);
            }

            if (!bookMarkConfs.Confs.ContainsKey(ctx.DestFileNameWithoutExt))
            {
                logger?.Invoke($"配置文件:没有找到{ctx.DestFileNameWithoutExt}宏替换器");
                return(false);
            }

            var bookMarks = bookMarkConfs.Confs[ctx.DestFileNameWithoutExt].Marks;

            if (bookMarks.ContainsKey(bookmarkstart.Name.Value.ToUpper()))
            {
                var bookMark = bookMarks[bookmarkstart.Name.Value.ToUpper()];
                switch (bookMark.Type)
                {
                case "TEXT":
                {
                    ctx.BookmarkText = ctx.PreprocessText(bookMark.Text);
                    ctx.BookmarkSql  = ctx.PreprocessSql(bookMark.Sql);

                    var bookmarkText = bookmarkstart.NextSibling <Run>();
                    var text         = bookmarkText?.GetFirstChild <Text>();
                    if (text != null)
                    {
                        _handler.CustomText(text, ctx, logger);
                        return(true);
                    }
                    else
                    {
                        logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:不是文本书签");
                    }
                }
                break;

                case "PICTURE":
                {
                    ctx.BookmarkPicUri = ctx.PreprocessPicUri(bookMark.PicUri);
                    ImagePart imagepart = null;
                    if (CheckPicture(bookmarkstart, ref imagepart))
                    {
                        _handler.CustomPicture(imagepart, ctx, logger);
                        return(true);
                    }
                    else
                    {
                        logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:不是图片书签");
                    }
                }
                break;

                case "TABLE":
                {
                    ctx.BookmarkSql = ctx.PreprocessSql(bookMark.Sql);
                    DocumentFormat.OpenXml.Wordprocessing.Table table;
                    if (CheckParentTable(bookmarkstart, out table))
                    {
                        _handler.CustomTable(table, ctx, logger);
                        return(true);
                    }
                    else
                    {
                        logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:不是表格书签");
                    }
                }
                break;

                default:
                {
                    logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:类型定义{bookMark.Type}不正确(TEXT/PICTURE/TABLE之一)");
                }
                    return(false);
                }

                logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:替换完成");
            }
            else
            {
                logger?.Invoke($"书签[{bookmarkstart.Name.Value}]:没有找到宏替换器");
            }
            return(false);
        }
        /// <summary>
        /// 书签配置文件解析
        /// </summary>
        /// <param name="logger">日志记录器</param>
        /// <returns></returns>
        public virtual BookMarkConfs EnsureBookMarkConf(Action <string> logger)
        {
            string bookMarkConfig = BookMarkConfigPath;

            if (!File.Exists(bookMarkConfig))
            {
                logger?.Invoke($"配置文件:{bookMarkConfig}不存在");
                return(null);
            }

            var xmlDoc = new XmlDocument();

            try
            {
                xmlDoc.Load(bookMarkConfig);
                logger?.Invoke($"配置文件:{bookMarkConfig}加载成功");
            }
            catch (Exception e)
            {
                logger?.Invoke(e.Message);
                Console.WriteLine(e);
                return(null);
            }

            var bookMarkConfs = new BookMarkConfs();
            var doc           = xmlDoc.DocumentElement;

            if (doc == null ||
                doc.ChildNodes.Count == 0)
            {
                logger?.Invoke($"配置文件:{bookMarkConfig}不存在任何节点信息");
                return(null);
            }

            foreach (XmlNode nodeMatch in doc.ChildNodes)
            {
                if (nodeMatch.Attributes == null)
                {
                    continue;
                }
                if (nodeMatch.ChildNodes.Count > 0)
                {
                    var templName = nodeMatch.Attributes["name"].Value;

                    var bookMarks = new BookMarks(templName);
                    bookMarkConfs.Confs.Add(templName, bookMarks);

                    foreach (XmlNode node in nodeMatch.ChildNodes)
                    {
                        if (node.Attributes == null)
                        {
                            continue;
                        }
                        var markname = node.Attributes["name"].Value.ToUpper();
                        var type     = node.Attributes["type"].Value.ToUpper();

                        var bookMark = new BookMark(type);
                        bookMarks.Marks.Add(markname, bookMark);

                        bookMark.Text   = node.Attributes["text"].Value;
                        bookMark.Sql    = node.Attributes["sql"].Value;
                        bookMark.PicUri = node.Attributes["url"].Value;
                    }
                }
            }

            return(bookMarkConfs);
        }