Browse Source

提交word解析为html

黄贺彬 6 years ago
parent
commit
69c568f8d9
100 changed files with 30395 additions and 5 deletions
  1. 4 0
      TEAMModelOS.Model/Evaluation/Dtos/ExerciseDto.cs
  2. 1 1
      TEAMModelOS.SDK/Context/Constant/ContentTypeDict.cs
  3. 44 0
      TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelper.cs
  4. 1 1
      TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelperCore.cs
  5. 36 0
      TEAMModelOS.SDK/Helper/Common/StringHelper/BufferHelper.cs
  6. 14 3
      TEAMModelOS.SDK/Helper/Common/StringHelper/HtmlHelper.cs
  7. 17 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushDIBColorsEnum.cs
  8. 24 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushEnum.cs
  9. 21 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushHSEnum.cs
  10. 34 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontCharsetEnum.cs
  11. 22 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontClipPrecisEnum.cs
  12. 21 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontFFEnum.cs
  13. 30 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontFWEnum.cs
  14. 25 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontOutPrecisEnum.cs
  15. 18 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontPitchEnum.cs
  16. 22 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontQualityEnum.cs
  17. 103 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/GdiEnum.cs
  18. 187 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/GdiUtils.cs
  19. 657 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdi.cs
  20. 36 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiBrush.cs
  21. 102 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiFont.cs
  22. 15 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiObject.cs
  23. 30 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPalette.cs
  24. 24 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPatternBrush.cs
  25. 33 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPen.cs
  26. 16 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiRegion.cs
  27. 22 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/PenEnum.cs
  28. 90 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/Point.cs
  29. 19 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/RegionEnum.cs
  30. 90 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/Size.cs
  31. 226 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/IO/DataInput.cs
  32. 132 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/Properties.cs
  33. 271 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgBrush.cs
  34. 976 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgDc.cs
  35. 532 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgFont.cs
  36. 2292 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgGdi.cs
  37. 77 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgObject.cs
  38. 67 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPalette.cs
  39. 50 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPatternBrush.cs
  40. 205 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPen.cs
  41. 149 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgRectRegion.cs
  42. 38 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgRegion.cs
  43. 84 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Util/Base64.cs
  44. 15 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Util/ImageUtil.cs
  45. 74 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfBrush.cs
  46. 85 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfConstants.cs
  47. 250 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfFont.cs
  48. 1647 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfGdi.cs
  49. 49 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfObject.cs
  50. 65 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPalette.cs
  51. 30 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfParseException.cs
  52. 821 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfParser.cs
  53. 52 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPatternBrush.cs
  54. 75 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPen.cs
  55. 92 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfRectRegion.cs
  56. 27 0
      TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfRegion.cs
  57. 19 0
      TEAMModelOS.SDK/Module/AzureBlob/Container/AzureBlobModel.cs
  58. 44 0
      TEAMModelOS.SDK/Module/AzureBlob/Implements/AzureBlobDBRepository.cs
  59. 1 0
      TEAMModelOS.SDK/Module/AzureBlob/Interfaces/IAzureBlobDBRepository.cs
  60. 602 0
      TEAMModelOS.SDK/Module/OpenXmlTool/ChartUpdater.cs
  61. 36 0
      TEAMModelOS.SDK/Module/OpenXmlTool/ColorParser.cs
  62. 67 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnit.cs
  63. 215 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitAtom.cs
  64. 77 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitGroup.cs
  65. 14 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitGroupType.cs
  66. 81 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitWord.cs
  67. 70 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/CorrelatedSequence.cs
  68. 16 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/CorrelationStatus.cs
  69. 15 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/PartSHA1HashAnnotation.cs
  70. 12 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WithHierarchicalGroupingKey.cs
  71. 401 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Internal.Methods.ComparisonUnits.cs
  72. 276 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Fields.cs
  73. 328 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Hashing.cs
  74. 1326 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Lcs.cs
  75. 331 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.PreProcessMarkup.cs
  76. 3232 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.ProduceDocument.cs
  77. 103 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Util.cs
  78. 36 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.NestedTypes.cs
  79. 227 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.Compare.cs
  80. 1002 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.Consolidate.cs
  81. 214 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.GetRevisions.cs
  82. 29 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.NestedTypes.cs
  83. 10 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerConsolidateSettings.cs
  84. 27 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerExtensions.cs
  85. 29 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerSettings.cs
  86. 59 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerUtil.cs
  87. 14 0
      TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlRevisedDocumentInfo.cs
  88. 857 0
      TEAMModelOS.SDK/Module/OpenXmlTool/DocumentAssembler.cs
  89. 4038 0
      TEAMModelOS.SDK/Module/OpenXmlTool/DocumentBuilder.cs
  90. 833 0
      TEAMModelOS.SDK/Module/OpenXmlTool/ExcelFormula.cs
  91. 416 0
      TEAMModelOS.SDK/Module/OpenXmlTool/FieldRetriever.cs
  92. 3510 0
      TEAMModelOS.SDK/Module/OpenXmlTool/FormattingAssembler.cs
  93. 240 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_Default.cs
  94. 233 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_fr_FR.cs
  95. 122 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_ru_RU.cs
  96. 213 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_sv_SE.cs
  97. 218 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_tr_TR.cs
  98. 130 0
      TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_zh_CN.cs
  99. 533 0
      TEAMModelOS.SDK/Module/OpenXmlTool/HtmlToWmlConverter.cs
  100. 0 0
      TEAMModelOS.SDK/Module/OpenXmlTool/HtmlToWmlConverterCore.cs

+ 4 - 0
TEAMModelOS.Model/Evaluation/Dtos/ExerciseDto.cs

@@ -11,6 +11,7 @@ namespace TEAMModelOS.Model.Evaluation.Dtos
     {
         public ExerciseDto()
         {
+            Children = new List<ExerciseDto>();
             Option = new List<CodeValue>();
             Answer = new List<string>();
         }
@@ -20,5 +21,8 @@ namespace TEAMModelOS.Model.Evaluation.Dtos
         public List<string> Answer { get; set; }
         public string Explain { get; set; }
         public string Type { get; set; }
+        public string PShaCode { get; set; }
+        [IgnoreMember]
+        public List<ExerciseDto> Children { get; set; }
     }
 }

+ 1 - 1
TEAMModelOS.SDK/Context/Constant/ContentTypeDict.cs

@@ -131,7 +131,7 @@ namespace TEAMModelOS.SDK.Context.Constant
             {".m", "text/x-matlab"},
             {".m2t", "video/mpeg"},
             {".m3u", "audio/x-mpegurl"},
-            {".m3u8", "audio/x-mpegurl"},
+            {".m3u8", "application/vnd.apple.mpegurl"},
             {".me", "text/x-troff-me"},
             {".mkv", "video/x-matroska"},
             {".ml", "text/x-ocaml"},

+ 44 - 0
TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelper.cs

@@ -352,6 +352,50 @@ namespace TEAMModelOS.SDK.Helper.Common.FileHelper
         }
         #endregion
 
+        // <summary>
+        ///直接删除指定目录下的所有文件及文件夹(保留目录)
+        /// </summary>
+        /// <param name="strPath">文件夹路径</param>
+        /// <returns>执行结果</returns>
+
+        public static void DeleteDirAndFiles(string file)
+        {
+            try
+            {
+                //去除文件夹和子文件的只读属性
+                //去除文件夹的只读属性
+                System.IO.DirectoryInfo fileInfo = new DirectoryInfo(file);
+                fileInfo.Attributes = FileAttributes.Normal & FileAttributes.Directory;
+                //去除文件的只读属性
+                System.IO.File.SetAttributes(file, System.IO.FileAttributes.Normal);
+                //判断文件夹是否还存在
+                if (Directory.Exists(file))
+                {
+                    foreach (string f in Directory.GetFileSystemEntries(file))
+                    {
+                        if (File.Exists(f))
+                        {
+                            //如果有子文件删除文件
+                            File.Delete(f);
+                            Console.WriteLine(f);
+                        }
+                        else
+                        {
+                            //循环递归删除子文件夹
+                            DeleteDirAndFiles(f);
+                        }
+                    }
+                    //删除空文件夹
+                    Directory.Delete(file);
+                }
+            }
+            catch (Exception ex) // 异常处理
+            {
+                Console.WriteLine(ex.Message.ToString());// 异常信息
+            }
+        }
+
+
         #region 删除指定目录及其所有子目录
         /// <summary>
         /// 删除指定目录及其所有子目录

+ 1 - 1
TEAMModelOS.SDK/Helper/Common/FileHelper/FileHelperCore.cs

@@ -116,7 +116,7 @@ namespace TEAMModelOS.SDK.Helper.Common.FileHelper
                 throw ex;
             }
         }
-
+        
         /// <summary>
         /// 清空目录下所有文件及子目录,依然保留该目录
         /// </summary>

+ 36 - 0
TEAMModelOS.SDK/Helper/Common/StringHelper/BufferHelper.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Helper.Common.StringHelper
+{
+    public class BufferHelper
+    {
+        public static byte[] BufferMerge(byte[] first, byte[] last) {
+            int len = first.Length + last.Length;
+            byte[] lenArr = BitConverter.GetBytes(len);
+            if (BitConverter.IsLittleEndian) {
+                Array.Reverse(lenArr);
+            }
+            byte[] resArr = new byte[lenArr.Length + first.Length + last.Length];
+            lenArr.CopyTo(resArr, 0);
+            first.CopyTo(resArr, lenArr.Length);
+            last.CopyTo(resArr, first.Length);
+            return resArr;
+        }
+
+        public static Stream StreamMerge(Stream streamA, Stream streamB) {
+            byte[] bufferA = new byte[streamA.Length];
+            byte[] bufferB = new byte[streamB.Length];
+            streamA.Read(bufferA, 0, bufferA.Length);
+            streamA.Seek(0, SeekOrigin.Begin);
+            streamB.Read(bufferB, 0, bufferB.Length);
+            streamB.Seek(0, SeekOrigin.Begin);
+            byte [] buffer= BufferMerge(bufferA, bufferB);
+            Stream stream = new MemoryStream(buffer);
+            return stream;
+
+        }
+    }
+}

+ 14 - 3
TEAMModelOS.SDK/Helper/Common/StringHelper/HtmlHelper.cs

@@ -17,20 +17,32 @@ namespace TEAMModelOS.SDK.Helper.Common.StringHelper
             }
             if (str.EndsWith("<p>"))
             {
-                str = str.Substring(0, str.Length - 1 - 6);
+                str = str.Substring(0, str.Length - 1 - 2);
             }
             if (str.EndsWith("<p >"))
             {
-                str = str.Substring(0, str.Length - 1 - 7);
+                str = str.Substring(0, str.Length - 1 - 3);
+            }
+            if (str.EndsWith("<p  >"))
+            {
+                str = str.Substring(0, str.Length - 1 - 4);
             }
             if (str.StartsWith("<p >") && !str.Contains("</p>"))
             {
                 str = str.Replace("<p >", "");
             }
+            if (str.StartsWith("<p  >") && !str.Contains("</p>"))
+            {
+                str = str.Replace("<p  >", "");
+            }
             if (str.StartsWith("<p>") && !str.Contains("</p>"))
             {
                 str = str.Replace("<p>", "");
             }
+            if (str.EndsWith("</p>") && !str.Contains("<p>")&& !str.Contains("<p >") && !str.Contains("<p  >")){
+                str = str.Replace("</p>", "");
+            }
+
             return str;
         }
 
@@ -62,7 +74,6 @@ namespace TEAMModelOS.SDK.Helper.Common.StringHelper
 
             // 搜索匹配的字符串   
             MatchCollection matches = regImg.Matches(sHtmlText);
-            int i = 0;
             //string[] sUrlList = new string[matches.Count];
             List<string> urls = new List<string>();
             // 取得匹配项列表   

+ 17 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushDIBColorsEnum.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// DIB-Colors enum.
+    /// </summary>
+    public enum BrushDIBColorsEnum
+    {
+        DIB_PAL_COLORS = 1,
+        DIB_RGB_COLORS = 0,
+    }
+}

+ 24 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushEnum.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Brush enum.
+    /// </summary>
+    public enum BrushBSEnum
+    {
+        BS_DIBPATTERN = 5,
+        BS_DIBPATTERN8X8 = 8,
+        BS_DIBPATTERNPT = 6,
+        BS_HATCHED = 2,
+        BS_HOLLOW = 1,
+        BS_NULL = 1,
+        BS_PATTERN = 3,
+        BS_PATTERN8X8 = 7,
+        BS_SOLID = 0,
+    }
+}

+ 21 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/BrushHSEnum.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// HS Brush enum
+    /// </summary>
+    public enum BrushHSEnum
+    {
+        HS_HORIZONTAL = 0,
+        HS_VERTICAL = 1,
+        HS_FDIAGONAL = 2,
+        HS_BDIAGONAL = 3,
+        HS_CROSS = 4,
+        HS_DIAGCROSS = 5,
+    }
+}

+ 34 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontCharsetEnum.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Charset Enum
+    /// </summary>
+    public enum FontCharsetEnum
+    {
+        ANSI_CHARSET = 0,
+        DEFAULT_CHARSET = 1,
+        SYMBOL_CHARSET = 2,
+        MAC_CHARSET = 77,
+        SHIFTJIS_CHARSET = 128,
+        HANGUL_CHARSET = 129,
+        JOHAB_CHARSET = 130,
+        GB2312_CHARSET = 134,
+        CHINESEBIG5_CHARSET = 136,
+        GREEK_CHARSET = 161,
+        TURKISH_CHARSET = 162,
+        VIETNAMESE_CHARSET = 163,
+        ARABIC_CHARSET = 178,
+        HEBREW_CHARSET = 177,
+        BALTIC_CHARSET = 186,
+        RUSSIAN_CHARSET = 204,
+        THAI_CHARSET = 222,
+        EASTEUROPE_CHARSET = 238,
+        OEM_CHARSET = 255,
+    }
+}

+ 22 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontClipPrecisEnum.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Clip Precis Enum
+    /// </summary>
+    public enum FontClipPrecisEnum
+    {
+        CLIP_DEFAULT_PRECIS = 0,
+        CLIP_CHARACTER_PRECIS = 1,
+        CLIP_STROKE_PRECIS = 2,
+        CLIP_MASK = 15,
+        CLIP_LH_ANGLES = 16,
+        CLIP_TT_ALWAYS = 32,
+        CLIP_EMBEDDED = 128,
+    }
+}

+ 21 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontFFEnum.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Font Family Enum.
+    /// </summary>
+    public enum FontFFEnum
+    {
+        FF_DONTCARE = 0,
+        FF_ROMAN = 16,
+        FF_SWISS = 32,
+        FF_MODERN = 48,
+        FF_SCRIPT = 64,
+        FF_DECORATIVE = 80,
+    }
+}

+ 30 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontFWEnum.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Font Weight Enum.
+    /// </summary>
+    public enum FontFWEnum
+    {
+        FW_DONTCARE = 0,
+        FW_THIN = 100,
+        FW_EXTRALIGHT = 200,
+        FW_ULTRALIGHT = 200,
+        FW_LIGHT = 300,
+        FW_NORMAL = 400,
+        FW_REGULAR = 400,
+        FW_MEDIUM = 500,
+        FW_SEMIBOLD = 600,
+        FW_DEMIBOLD = 600,
+        FW_BOLD = 700,
+        FW_EXTRABOLD = 800,
+        FW_ULTRABOLD = 800,
+        FW_HEAVY = 900,
+        FW_BLACK = 900,
+    }
+}

+ 25 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontOutPrecisEnum.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Out Preciss Enum
+    /// </summary>
+    public enum FontOutPrecisEnum
+    {
+        OUT_DEFAULT_PRECIS = 0,
+        OUT_STRING_PRECIS = 1,
+        OUT_CHARACTER_PRECIS = 2,
+        OUT_STROKE_PRECIS = 3,
+        OUT_TT_PRECIS = 4,
+        OUT_DEVICE_PRECIS = 5,
+        OUT_RASTER_PRECIS = 6,
+        OUT_TT_ONLY_PRECIS = 7,
+        OUT_OUTLINE_PRECIS = 8,
+        OUT_SCREEN_OUTLINE_PRECIS = 9,
+    }
+}

+ 18 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontPitchEnum.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Pitch Enum.
+    /// </summary>
+    public enum FontPitchEnum
+    {
+        DEFAULT_PITCH = 0,
+        FIXED_PITCH = 1,
+        VARIABLE_PITCH = 2,
+    }
+}

+ 22 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/FontQualityEnum.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Quality Enum.
+    /// </summary>
+    public enum FontQualityEnum
+    {
+        DEFAULT_QUALITY = 0,
+        DRAFT_QUALITY = 1,
+        PROOF_QUALITY = 2,
+        NONANTIALIASED_QUALITY = 3,
+        ANTIALIASED_QUALITY = 4,
+        CLEARTYPE_QUALITY = 5, // Windows XP only
+    }
+
+}

+ 103 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/GdiEnum.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface Enum
+    /// </summary>
+    public enum GdiEnum
+    {
+        OPAQUE = 2,
+        TRANSPARENT = 1,
+
+        TA_BASELINE = 24,
+        TA_BOTTOM = 8,
+        TA_TOP = 0,
+        TA_CENTER = 6,
+        TA_LEFT = 0,
+        TA_RIGHT = 2,
+        TA_NOUPDATECP = 0,
+        TA_RTLREADING = 256,
+        TA_UPDATECP = 1,
+        VTA_BASELINE = 24,
+        VTA_CENTER = 6,
+
+        ETO_CLIPPED = 4,
+        ETO_NUMERICSLOCAL = 1024,
+        ETO_NUMERICSLATIN = 2048,
+        ETO_GLYPH_INDEX = 16,
+        ETO_OPAQUE = 2,
+        ETO_PDY = 8192,
+        ETO_RTLREADING = 128,
+        ETO_IGNORELANGUAGE = 4096,
+
+        MM_ANISOTROPIC = 8,
+        MM_HIENGLISH = 5,
+        MM_HIMETRIC = 3,
+        MM_ISOTROPIC = 7,
+        MM_LOENGLISH = 4,
+        MM_LOMETRIC = 2,
+        MM_TEXT = 1,
+        MM_TWIPS = 6,
+
+        STRETCH_ANDSCANS = 2,
+        STRETCH_DELETESCANS = 3,
+        STRETCH_HALFTONE = 4,
+        STRETCH_ORSCANS = 2,
+        BLACKONWHITE = 2,
+        COLORONCOLOR = 3,
+        HALFTONE = 4,
+        WHITEONBLACK = 2,
+
+        ALTERNATE = 1,
+        WINDING = 2,
+
+        R2_BLACK = 1,
+        R2_COPYPEN = 13,
+        R2_MASKNOTPEN = 3,
+        R2_MASKPEN = 9,
+        R2_MASKPENNOT = 5,
+        R2_MERGENOTPEN = 12,
+        R2_MERGEPEN = 15,
+        R2_MERGEPENNOT = 14,
+        R2_NOP = 11,
+        R2_NOT = 6,
+        R2_NOTCOPYPEN = 4,
+        R2_NOTMASKPEN = 8,
+        R2_NOTMERGEPEN = 2,
+        R2_NOTXORPEN = 10,
+        R2_WHITE = 16,
+        R2_XORPEN = 7,
+
+        BLACKNESS = 66,
+        DSTINVERT = 5570569,
+        MERGECOPY = 12583114,
+        MERGEPAINT = 12255782,
+        NOTSRCCOPY = 3342344,
+        NOTSRCERASE = 1114278,
+        PATCOPY = 15728673,
+        PATINVERT = 5898313,
+        PATPAINT = 16452105,
+        SRCAND = 8913094,
+        SRCCOPY = 13369376,
+        SRCERASE = 4457256,
+        SRCINVERT = 6684742,
+        SRCPAINT = 15597702,
+        WHITENESS = 16711778,
+
+        DIB_RGB_COLORS = 0,
+        DIB_PAL_COLORS = 1,
+
+        LAYOUT_BITMAPORIENTATIONPRESERVED = 8,
+        LAYOUT_RTL = 1,
+
+        ABSOLUTE = 1,
+        RELATIVE = 2,
+
+        ASPECT_FILTERING = 1,
+    }
+}

+ 187 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/GdiUtils.cs

@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Utils functions.
+    /// </summary>
+    public class GdiUtils
+    {
+        #region Local Variables
+
+        private static int[,] FBA_SHIFT_JIS = new int[,] { { 0x81, 0x9F }, { 0xE0, 0xFC } };
+        private static int[,] FBA_HANGUL_CHARSET = new int[,] { { 0x80, 0xFF } };
+        private static int[,] FBA_JOHAB_CHARSET = new int[,] { { 0x80, 0xFF } };
+        private static int[,] FBA_GB2312_CHARSET = new int[,] { { 0x80, 0xFF } };
+        private static int[,] FBA_CHINESEBIG5_CHARSET = new int[,] { { 0xA1, 0xFE } };
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Convert byte array to string using specified charset.
+        /// </summary>
+        /// <param name="chars"></param>
+        /// <param name="charset"></param>
+        /// <returns></returns>
+        public static string ConvertString(byte[] chars, int charset)
+        {
+            string str = null;
+
+            int length = 0;
+            while (length < chars.Count() && chars[length] != 0)
+            {
+                length++;
+            }
+
+            try
+            {
+                str = System.Text.Encoding.GetEncoding(GetCharset(charset)).GetString(chars).Substring(0, length);
+            }
+            catch (Exception ex)
+            {
+                try
+                {
+                    str = System.Text.Encoding.GetEncoding("US-ASCII").GetString(chars).Substring(0, length);
+                }
+                catch (Exception ex2)
+                {
+                    throw new Exception(ex2.Message);
+                }
+            }
+            return str;
+        }
+
+        /// <summary>
+        /// Get specified charset.
+        /// </summary>
+        /// <param name="charset"></param>
+        /// <returns></returns>
+        public static string GetCharset(int charset)
+        {
+            switch (charset)
+            {
+                case (int)FontCharsetEnum.ANSI_CHARSET:
+                    return "Cp1252";
+                case (int)FontCharsetEnum.SYMBOL_CHARSET:
+                    return "Cp1252";
+                case (int)FontCharsetEnum.MAC_CHARSET:
+                    return "MacRoman";
+                case (int)FontCharsetEnum.SHIFTJIS_CHARSET:
+                    return "MS932";
+                case (int)FontCharsetEnum.HANGUL_CHARSET:
+                    return "MS949";
+                case (int)FontCharsetEnum.JOHAB_CHARSET:
+                    return "Johab";
+                case (int)FontCharsetEnum.GB2312_CHARSET:
+                    return "MS936";
+                case (int)FontCharsetEnum.CHINESEBIG5_CHARSET:
+                    return "MS950";
+                case (int)FontCharsetEnum.GREEK_CHARSET:
+                    return "Cp1253";
+                case (int)FontCharsetEnum.TURKISH_CHARSET:
+                    return "Cp1254";
+                case (int)FontCharsetEnum.VIETNAMESE_CHARSET:
+                    return "Cp1258";
+                case (int)FontCharsetEnum.HEBREW_CHARSET:
+                    return "Cp1255";
+                case (int)FontCharsetEnum.ARABIC_CHARSET:
+                    return "Cp1256";
+                case (int)FontCharsetEnum.BALTIC_CHARSET:
+                    return "Cp1257";
+                case (int)FontCharsetEnum.RUSSIAN_CHARSET:
+                    return "Cp1251";
+                case (int)FontCharsetEnum.THAI_CHARSET:
+                    return "MS874";
+                case (int)FontCharsetEnum.EASTEUROPE_CHARSET:
+                    return "Cp1250";
+                case (int)FontCharsetEnum.OEM_CHARSET:
+                    return "Cp1252";
+                default:
+                    return "Cp1252";
+            }
+        }
+
+        /// <summary>
+        /// Get language using specified charset.
+        /// </summary>
+        /// <param name="charset"></param>
+        /// <returns></returns>
+        public static string GetLanguage(int charset)
+        {
+            switch (charset)
+            {
+                case (int)FontCharsetEnum.ANSI_CHARSET:
+                    return "en";
+                case (int)FontCharsetEnum.SYMBOL_CHARSET:
+                    return "en";
+                case (int)FontCharsetEnum.MAC_CHARSET:
+                    return "en";
+                case (int)FontCharsetEnum.SHIFTJIS_CHARSET:
+                    return "ja";
+                case (int)FontCharsetEnum.HANGUL_CHARSET:
+                    return "ko";
+                case (int)FontCharsetEnum.JOHAB_CHARSET:
+                    return "ko";
+                case (int)FontCharsetEnum.GB2312_CHARSET:
+                    return "zh-CN";
+                case (int)FontCharsetEnum.CHINESEBIG5_CHARSET:
+                    return "zh-TW";
+                case (int)FontCharsetEnum.GREEK_CHARSET:
+                    return "el";
+                case (int)FontCharsetEnum.TURKISH_CHARSET:
+                    return "tr";
+                case (int)FontCharsetEnum.VIETNAMESE_CHARSET:
+                    return "vi";
+                case (int)FontCharsetEnum.HEBREW_CHARSET:
+                    return "iw";
+                case (int)FontCharsetEnum.ARABIC_CHARSET:
+                    return "ar";
+                case (int)FontCharsetEnum.BALTIC_CHARSET:
+                    return "bat";
+                case (int)FontCharsetEnum.RUSSIAN_CHARSET:
+                    return "ru";
+                case (int)FontCharsetEnum.THAI_CHARSET:
+                    return "th";
+                case (int)FontCharsetEnum.EASTEUROPE_CHARSET:
+                    return null;
+                case (int)FontCharsetEnum.OEM_CHARSET:
+                    return null;
+                default:
+                    return null;
+            }
+        }
+
+
+        /// <summary>
+        /// Get the first byte area specified charset.
+        /// </summary>
+        /// <param name="charset"></param>
+        /// <returns></returns>
+        public static int[,] GetFirstByteArea(int charset)
+        {
+            switch (charset)
+            {
+                case (int)FontCharsetEnum.SHIFTJIS_CHARSET:
+                    return FBA_SHIFT_JIS;
+                case (int)FontCharsetEnum.HANGUL_CHARSET:
+                    return FBA_HANGUL_CHARSET;
+                case (int)FontCharsetEnum.JOHAB_CHARSET:
+                    return FBA_JOHAB_CHARSET;
+                case (int)FontCharsetEnum.GB2312_CHARSET:
+                    return FBA_GB2312_CHARSET;
+                case (int)FontCharsetEnum.CHINESEBIG5_CHARSET:
+                    return FBA_CHINESEBIG5_CHARSET;
+                default:
+                    return null;
+            }
+        }
+
+        #endregion
+    }
+}

+ 657 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdi.cs

@@ -0,0 +1,657 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents graphics elements
+    /// </summary>
+    public interface IGdi
+    {
+        /// <summary>
+        /// Create header document.
+        /// </summary>
+        /// <param name="vsx"></param>
+        /// <param name="vsy"></param>
+        /// <param name="vex"></param>
+        /// <param name="vey"></param>
+        /// <param name="dpi"></param>
+        void PlaceableHeader(int vsx, int vsy, int vex, int vey, int dpi);
+
+        /// <summary>
+        /// Initialize Header document.
+        /// </summary>
+        void Header();
+
+        /// <summary>
+        /// Element is put inside a shape element and defines how an attribute of an element changes over the animation. 
+        /// The attribute will change from the initial value to the end value in the duration specified.
+        /// </summary>
+        /// <param name="palette"></param>
+        /// <param name="startIndex"></param>
+        /// <param name="entries"></param>
+        void AnimatePalette(IGdiPalette palette, int startIndex, int[] entries);
+
+        /// <summary>
+        /// Draws an elliptical arc.
+        /// </summary>
+        /// <param name="sxr"></param>
+        /// <param name="syr"></param>
+        /// <param name="exr"></param>
+        /// <param name="eyr"></param>
+        /// <param name="sxa"></param>
+        /// <param name="sya"></param>
+        /// <param name="exa"></param>
+        /// <param name="eya"></param>
+        void Arc(int sxr, int syr, int exr, int eyr,
+               int sxa, int sya, int exa, int eya);
+
+        /// <summary>
+        /// Execute a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="rop"></param>
+        void BitBlt(byte[] image, int dx, int dy, int dw, int dh, int sx, int sy, long rop);
+
+        /// <summary>
+        /// Draws a chord (a region bounded by the intersection of an ellipse and a line segment, called a secant).
+        /// The chord is outlined by using the current pen and filled by using the current brush.
+        /// </summary>
+        /// <param name="sxr"></param>
+        /// <param name="syr"></param>
+        /// <param name="exr"></param>
+        /// <param name="eyr"></param>
+        /// <param name="sxa"></param>
+        /// <param name="sya"></param>
+        /// <param name="exa"></param>
+        /// <param name="eya"></param>
+        void Chord(int sxr, int syr, int exr, int eyr,
+                 int sxa, int sya, int exa, int eya);
+
+        /// <summary>
+        /// Creates a logical brush that has the specified style, color, and pattern.
+        /// </summary>
+        /// <param name="style"></param>
+        /// <param name="color"></param>
+        /// <param name="hatch"></param>
+        /// <returns></returns>
+        IGdiBrush CreateBrushIndirect(int style, int color, int hatch);
+
+        /// <summary>
+        /// Creates a logical font that has the specified characteristics. 
+        /// The font can subsequently be selected as the current font for any device context.
+        /// </summary>
+        /// <param name="height"></param>
+        /// <param name="width"></param>
+        /// <param name="escapement"></param>
+        /// <param name="orientation"></param>
+        /// <param name="weight"></param>
+        /// <param name="italic"></param>
+        /// <param name="underline"></param>
+        /// <param name="strikeout"></param>
+        /// <param name="charset"></param>
+        /// <param name="outPrecision"></param>
+        /// <param name="clipPrecision"></param>
+        /// <param name="quality"></param>
+        /// <param name="pitchAndFamily"></param>
+        /// <param name="faceName"></param>
+        /// <returns></returns>
+        IGdiFont CreateFontIndirect(int height, int width, int escapement,
+                         int orientation, int weight,
+                         bool italic, bool underline, bool strikeout,
+                         int charset, int outPrecision, int clipPrecision,
+                         int quality, int pitchAndFamily, byte[] faceName);
+
+        /// <summary>
+        /// Creates a logical palette.
+        /// </summary>
+        /// <param name="version"></param>
+        /// <param name="palEntry"></param>
+        /// <returns></returns>
+        IGdiPalette CreatePalette(int version, int[] palEntry);
+
+        /// <summary>
+        /// Creates a logical brush with the specified bitmap pattern.
+        /// The bitmap can be a DIB section bitmap, which is created by the CreateDIBSection function, or it can be a device-dependent bitmap.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <returns></returns>
+        IGdiPatternBrush CreatePatternBrush(byte[] image);
+
+        /// <summary>
+        /// Creates a logical cosmetic pen that has the style, width, and color specified in a structure.
+        /// </summary>
+        /// <param name="style"></param>
+        /// <param name="width"></param>
+        /// <param name="color"></param>
+        /// <returns></returns>
+        IGdiPen CreatePenIndirect(int style, int width, int color);
+
+        /// <summary>
+        /// Creates a rectangular region.
+        /// </summary>
+        /// <param name="left"></param>
+        /// <param name="top"></param>
+        /// <param name="right"></param>
+        /// <param name="bottom"></param>
+        /// <returns></returns>
+        IGdiRegion CreateRectRgn(int left, int top, int right, int bottom);
+
+        /// <summary>
+        /// Deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. 
+        /// After the object is deleted, the specified handle is no longer valid.
+        /// </summary>
+        /// <param name="obj"></param>
+        void DeleteObject(IGdiObject obj);
+
+        /// <summary>
+        /// Function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="rop"></param>
+        void DibBitBlt(byte[] image, int dx, int dy, int dw, int dh,
+               int sx, int sy, long rop);
+
+        /// <summary>
+        /// Create Dib Pattern Brush object instance.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <param name="usage"></param>
+        /// <returns></returns>
+        IGdiPatternBrush DibCreatePatternBrush(byte[] image, int usage);
+
+        /// <summary>
+        /// Copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. 
+        /// The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="sw"></param>
+        /// <param name="sh"></param>
+        /// <param name="rop"></param>
+        void DibStretchBlt(byte[] image, int dx, int dy, int dw, int dh,
+               int sx, int sy, int sw, int sh, long rop);
+
+        /// <summary>
+        /// Draws an ellipse. 
+        /// The center of the ellipse is the center of the specified bounding rectangle.
+        /// The ellipse is outlined by using the current pen and is filled by using the current brush.
+        /// </summary>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="ex"></param>
+        /// <param name="ey"></param>
+        void Ellipse(int sx, int sy, int ex, int ey);
+
+        /// <summary>
+        /// Enables an application to access the system-defined device capabilities that are not available through GDI. 
+        /// Escape calls made by an application are translated and sent to the driver.
+        /// </summary>
+        /// <param name="data"></param>
+        void Escape(byte[] data);
+
+        /// <summary>
+        ///  Creates a new clipping region that consists of the existing clipping region minus the specified rectangle.
+        /// </summary>
+        /// <param name="left"></param>
+        /// <param name="top"></param>
+        /// <param name="right"></param>
+        /// <param name="bottom"></param>
+        /// <returns></returns>
+        int ExcludeClipRect(int left, int top, int right, int bottom);
+
+        /// <summary>
+        /// Fills an area of the display surface with the current brush.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="color"></param>
+        /// <param name="type"></param>
+        void ExtFloodFill(int x, int y, int color, int type);
+
+        /// <summary>
+        /// Draws text using the currently selected font, background color, and text color. 
+        /// You can optionally provide dimensions to be used for clipping, opaquing, or both.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="options"></param>
+        /// <param name="rect"></param>
+        /// <param name="text"></param>
+        /// <param name="lpdx"></param>
+        void ExtTextOut(int x, int y, int options, int[] rect, byte[] text, int[] lpdx);
+
+        /// <summary>
+        /// Fills a region by using the specified brush.
+        /// </summary>
+        /// <param name="rgn"></param>
+        /// <param name="brush"></param>
+        void FillRgn(IGdiRegion rgn, IGdiBrush brush);
+
+        /// <summary>
+        /// Fills an area of the display surface with the current brush. 
+        /// The area is assumed to be bounded as specified by the crFill parameter.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="color"></param>
+        void FloodFill(int x, int y, int color);
+
+        /// <summary>
+        /// Draws a border around the specified region by using the specified brush.
+        /// </summary>
+        /// <param name="rgn"></param>
+        /// <param name="brush"></param>
+        /// <param name="w"></param>
+        /// <param name="h"></param>
+        void FrameRgn(IGdiRegion rgn, IGdiBrush brush, int w, int h);
+
+        /// <summary>
+        /// Creates a new clipping region from the intersection of the current clipping region and the specified rectangle.
+        /// </summary>
+        /// <param name="left"></param>
+        /// <param name="top"></param>
+        /// <param name="right"></param>
+        /// <param name="bottom"></param>
+        void IntersectClipRect(int left, int top, int right, int bottom);
+
+        /// <summary>
+        /// Inverts the colors in the specified region.
+        /// </summary>
+        /// <param name="rgn"></param>
+        void InvertRgn(IGdiRegion rgn);
+
+        /// <summary>
+        /// Draws a line from the current position up to, but not including, the specified point.
+        /// </summary>
+        /// <param name="ex"></param>
+        /// <param name="ey"></param>
+        void LineTo(int ex, int ey);
+
+        /// <summary>
+        /// Updates the current position to the specified point and optionally returns the previous position.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        void MoveToEx(int x, int y, Point old);
+
+        /// <summary>
+        /// Moves the clipping region of a device context by the specified offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        void OffsetClipRgn(int x, int y);
+
+        /// <summary>
+        /// Modifies the viewport origin for a device context using the specified horizontal and vertical offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="point"></param>
+        void OffsetViewportOrgEx(int x, int y, Point point);
+
+        /// <summary>
+        /// Modifies the window origin for a device context using the specified horizontal and vertical offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="point"></param>
+        void OffsetWindowOrgEx(int x, int y, Point point);
+
+        /// <summary>
+        /// Paints the specified region by using the brush currently selected into the device context.
+        /// </summary>
+        /// <param name="rgn"></param>
+        void PaintRgn(IGdiRegion rgn);
+
+        /// <summary>
+        /// Paints the specified rectangle using the brush that is currently selected into the specified device context.
+        /// The brush color and the surface color or colors are combined by using the specified raster operation.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        /// <param name="rop"></param>
+        void PatBlt(int x, int y, int width, int height, long rop);
+
+        /// <summary>
+        /// Draws a pie-shaped wedge bounded by the intersection of an ellipse and two radials. 
+        /// The pie is outlined by using the current pen and filled by using the current brush.
+        /// </summary>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="ex"></param>
+        /// <param name="ey"></param>
+        /// <param name="sxr"></param>
+        /// <param name="syr"></param>
+        /// <param name="exr"></param>
+        /// <param name="eyr"></param>
+        void Pie(int sx, int sy, int ex, int ey, int sxr, int syr, int exr, int eyr);
+
+        /// <summary>
+        /// Draws a polygon consisting of two or more vertices connected by straight lines. 
+        /// The polygon is outlined by using the current pen and filled by using the current brush and polygon fill mode.
+        /// </summary>
+        /// <param name="points"></param>
+        void Polygon(Point[] points);
+
+        /// <summary>
+        /// Element is an SVG basic shape that creates straight lines connecting several points. 
+        /// Typically a polyline is used to create open shapes as the last point doesn't have to be connected to the first point. 
+        /// </summary>
+        /// <param name="points"></param>
+        void Polyline(Point[] points);
+
+        /// <summary>
+        ///  Draws a series of closed polygons. 
+        ///  Each polygon is outlined by using the current pen and filled by using the current brush and polygon fill mode. 
+        ///  The polygons drawn by this function can overlap.
+        /// </summary>
+        /// <param name="points"></param>
+        void PolyPolygon(Point[][] points);
+
+        /// <summary>
+        /// Maps palette entries from the current logical palette to the system palette.
+        /// </summary>
+        void RealizePalette();
+
+        /// <summary>
+        /// Restores a device context (DC) to the specified state. 
+        /// The DC is restored by popping state information off a stack created by earlier calls to the SaveDC function.
+        /// </summary>
+        /// <param name="savedDC"></param>
+        void RestoreDC(int savedDC);
+
+        /// <summary>
+        /// Draws a rectangle. 
+        /// The rectangle is outlined by using the current pen and filled by using the current brush.
+        /// </summary>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="ex"></param>
+        /// <param name="ey"></param>
+        void Rectangle(int sx, int sy, int ex, int ey);
+
+        /// <summary>
+        /// Increases or decreases the size of a logical palette based on the specified value.
+        /// </summary>
+        /// <param name="palette"></param>
+        void ResizePalette(IGdiPalette palette);
+
+        /// <summary>
+        /// Draws a rectangle with rounded corners. 
+        /// The rectangle is outlined by using the current pen and filled by using the current brush.
+        /// </summary>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="ex"></param>
+        /// <param name="ey"></param>
+        /// <param name="rw"></param>
+        /// <param name="rh"></param>
+        void RoundRect(int sx, int sy, int ex, int ey, int rw, int rh);
+
+        /// <summary>
+        /// Save device context (DC).
+        /// </summary>
+        void SeveDC();
+
+        /// <summary>
+        /// Modifies the viewport for a device context using the ratios formed by the specified multiplicands and divisors.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="xd"></param>
+        /// <param name="y"></param>
+        /// <param name="yd"></param>
+        /// <param name="old"></param>
+        void ScaleViewportExtEx(int x, int xd, int y, int yd, Size old);
+
+        /// <summary>
+        /// Modifies the window for a device context using the ratios formed by the specified multiplicands and divisors.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="xd"></param>
+        /// <param name="y"></param>
+        /// <param name="yd"></param>
+        /// <param name="old"></param>
+        void ScaleWindowExtEx(int x, int xd, int y, int yd, Size old);
+
+        /// <summary>
+        /// Selects a region as the current clipping region for the specified device context.
+        /// </summary>
+        /// <param name="rgn"></param>
+        void SelectClipRgn(IGdiRegion rgn);
+
+        /// <summary>
+        /// Selects an object into the specified device context (DC). The new object replaces the previous object of the same type.
+        /// </summary>
+        /// <param name="obj"></param>
+        void SelectObject(IGdiObject obj);
+
+        /// <summary>
+        /// Selects the specified logical palette into a device context.
+        /// </summary>
+        /// <param name="palette"></param>
+        /// <param name="mode"></param>
+        void SelectPalette(IGdiPalette palette, bool mode);
+
+        /// <summary>
+        /// Sets the current background color to the specified color value, or to the nearest physical color if the device cannot represent the specified color value.
+        /// </summary>
+        /// <param name="color"></param>
+        void SetBkColor(int color);
+
+        /// <summary>
+        /// Sets the background mix mode of the specified device context. 
+        /// The background mix mode is used with text, hatched brushes, and pen styles that are not solid lines.
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetBkMode(int mode);
+
+        /// <summary>
+        /// Sets the pixels in the specified rectangle on the device that is associated with the destination device context using color data from a DIB, JPEG, or PNG image.
+        /// </summary>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="startscan"></param>
+        /// <param name="scanlines"></param>
+        /// <param name="image"></param>
+        /// <param name="colorUse"></param>
+        void SetDIBitsToDevice(int dx, int dy, int dw, int dh, int sx, int sy,
+                       int startscan, int scanlines, byte[] image, int colorUse);
+
+        /// <summary>
+        /// Changes the layout of a device context (DC).
+        /// </summary>
+        /// <param name="layout"></param>
+        void SetLayout(long layout);
+
+        /// <summary>
+        /// Sets the mapping mode of the specified device context. 
+        /// The mapping mode defines the unit of measure used to transform page-space units into device-space units, and also defines the orientation of the device's x and y axes.
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetMapMode(int mode);
+
+        /// <summary>
+        /// Alters the algorithm the font mapper uses when it maps logical fonts to physical fonts.
+        /// </summary>
+        /// <param name="flags"></param>
+        void SetMapperFlags(long flags);
+
+        /// <summary>
+        /// Sets RGB (red, green, blue) color values and flags in a range of entries in a logical palette.
+        /// </summary>
+        /// <param name="palette"></param>
+        /// <param name="startIndex"></param>
+        /// <param name="entries"></param>
+        void SetPaletteEntries(IGdiPalette palette, int startIndex, int[] entries);
+
+        /// <summary>
+        /// Sets the pixel at the specified coordinates to the specified color.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="color"></param>
+        void SetPixel(int x, int y, int color);
+
+        /// <summary>
+        /// Sets the polygon fill mode for functions that fill polygons.
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetPolyFillMode(int mode);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetRelAbs(int mode);
+
+        /// <summary>
+        /// Sets the current foreground mix mode. 
+        /// GDI uses the foreground mix mode to combine pens and interiors of filled objects with the colors already on the screen. 
+        /// The foreground mix mode defines how colors from the brush or pen and the colors in the existing image are to be combined.
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetROP2(int mode);
+
+        /// <summary>
+        /// Sets the bitmap stretching mode in the specified device context.
+        /// </summary>
+        /// <param name="mode"></param>
+        void SetStretchBltMode(int mode);
+        
+        /// <summary>
+        /// Sets the text-alignment flags for the specified device context.
+        /// </summary>
+        /// <param name="align"></param>
+        void SetTextAlign(int align);
+
+        /// <summary>
+        /// Sets the intercharacter spacing. 
+        /// Intercharacter spacing is added to each character, including break characters, when the system writes a line of text.
+        /// </summary>
+        /// <param name="extra"></param>
+        void SetTextCharacterExtra(int extra);
+
+        /// <summary>
+        /// Sets the text color for the specified device context to the specified color.
+        /// </summary>
+        /// <param name="color"></param>
+        void SetTextColor(int color);
+
+        /// <summary>
+        /// Specifies the amount of space the system should add to the break characters in a string of text. 
+        /// The space is added when an application calls the TextOut or ExtTextOut functions.
+        /// </summary>
+        /// <param name="breakExtra"></param>
+        /// <param name="breakCount"></param>
+        void SetTextJustification(int breakExtra, int breakCount);
+
+        /// <summary>
+        /// Sets the horizontal and vertical extents of the viewport for a device context by using the specified values.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        void SetViewportExtEx(int x, int y, Size old);
+
+        /// <summary>
+        /// Specifies which device point maps to the window origin (0,0).
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        void SetViewportOrgEx(int x, int y, Point old);
+
+        /// <summary>
+        /// Sets the horizontal and vertical extents of the window for a device context by using the specified values.
+        /// </summary>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        /// <param name="old"></param>
+        void SetWindowExtEx(int width, int height, Size old);
+
+        /// <summary>
+        /// Specifies which window point maps to the viewport origin (0,0).
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        void SetWindowOrgEx(int x, int y, Point old);
+
+        /// <summary>
+        /// Copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. 
+        /// The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
+        /// </summary>
+        /// <param name="image"></param>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="sw"></param>
+        /// <param name="sh"></param>
+        /// <param name="rop"></param>
+        void StretchBlt(byte[] image, int dx, int dy, int dw, int dh,
+               int sx, int sy, int sw, int sh, long rop);
+
+        /// <summary>
+        /// Copies the color data for a rectangle of pixels in a DIB, JPEG, or PNG image to the specified destination rectangle. 
+        /// If the destination rectangle is larger than the source rectangle, this function stretches the rows and columns of color data to fit the destination rectangle. If the destination rectangle is smaller than the source rectangle, this function compresses the rows and columns by using the specified raster operation.
+        /// </summary>
+        /// <param name="dx"></param>
+        /// <param name="dy"></param>
+        /// <param name="dw"></param>
+        /// <param name="dh"></param>
+        /// <param name="sx"></param>
+        /// <param name="sy"></param>
+        /// <param name="sw"></param>
+        /// <param name="sh"></param>
+        /// <param name="image"></param>
+        /// <param name="usage"></param>
+        /// <param name="rop"></param>
+        void StretchDIBits(int dx, int dy, int dw, int dh,
+                       int sx, int sy, int sw, int sh,
+                       byte[] image, int usage, long rop);
+
+        /// <summary>
+        /// Writes a character string at the specified location, using the currently selected font, background color, and text color.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="text"></param>
+        void TextOut(int x, int y, byte[] text);
+
+        /// <summary>
+        /// 
+        /// </summary>
+        void Footer();
+    }
+}

+ 36 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiBrush.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Brush object.
+    /// </summary>
+    public interface IGdiBrush : IGdiObject
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object Style.
+        /// </summary>
+        /// <returns></returns>
+        int Style { get; }
+
+        /// <summary>
+        /// Object Color.
+        /// </summary>
+        /// <returns></returns>
+        int Color {get;}
+
+        /// <summary>
+        /// Object Hatch.
+        /// </summary>
+        /// <returns></returns>
+        int Hatch { get; }
+
+        #endregion
+    }
+}

+ 102 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiFont.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Font object.
+    /// </summary>
+    public interface IGdiFont : IGdiObject
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object Height.
+        /// </summary>
+        /// <returns></returns>
+        int Height {get;}
+
+        /// <summary>
+        /// Object Width.
+        /// </summary>
+        /// <returns></returns>
+        int Width { get; }
+
+        /// <summary>
+        /// Object Escapement.
+        /// </summary>
+        /// <returns></returns>
+        int Escapement { get; }
+
+        /// <summary>
+        /// Object Orientation.
+        /// </summary>
+        /// <returns></returns>
+        int Orientation { get; }
+
+        /// <summary>
+        /// Object Weight.
+        /// </summary>
+        /// <returns></returns>
+        int Weight { get; }
+
+        /// <summary>
+        /// Italic font.
+        /// </summary>
+        /// <returns></returns>
+        bool IsItalic { get; }
+
+        /// <summary>
+        /// Underlined font.
+        /// </summary>
+        /// <returns></returns>
+        bool IsUnderlined { get; }
+
+        /// <summary>
+        /// Striked font.
+        /// </summary>
+        /// <returns></returns>
+        bool IsStrikedOut { get; }
+
+        /// <summary>
+        /// Object Charset.
+        /// </summary>
+        /// <returns></returns>
+        int Charset { get; }
+
+        /// <summary>
+        /// Object out precision.
+        /// </summary>
+        /// <returns></returns>
+        int OutPrecision { get; }
+
+        /// <summary>
+        /// Object clip precision.
+        /// </summary>
+        /// <returns></returns>
+        int ClipPrecision { get; }
+
+        /// <summary>
+        /// Object Quality.
+        /// </summary>
+        /// <returns></returns>
+        int Quality { get; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <returns></returns>
+        int PitchAndFamily { get; }
+
+        /// <summary>
+        /// Font face name.
+        /// </summary>
+        /// <returns></returns>
+        string FaceName { get; }
+
+        #endregion
+    }
+}

+ 15 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiObject.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphic Device Interface - Object base.
+    /// </summary>
+    public interface IGdiObject
+    {
+    }
+}

+ 30 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPalette.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Palette object.
+    /// </summary>
+    public interface IGdiPalette : IGdiObject
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object Version.
+        /// </summary>
+        /// <returns></returns>
+        int Version { get; }
+
+        /// <summary>
+        /// Object Entries.
+        /// </summary>
+        /// <returns></returns>
+        int[] Entries { get; }
+
+        #endregion
+    }
+}

+ 24 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPatternBrush.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Pattern Brush object.
+    /// </summary>
+    public interface IGdiPatternBrush : IGdiObject
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object Patterns.
+        /// </summary>
+        /// <returns></returns>
+        byte[] Pattern { get; }
+
+        #endregion
+    }
+}

+ 33 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiPen.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Pen object.
+    /// </summary>
+    public interface IGdiPen : IGdiObject
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object Style.
+        /// </summary>
+        int Style { get; }
+
+        /// <summary>
+        /// Object Width.
+        /// </summary>
+        int Width { get; }
+
+        /// <summary>
+        /// Object Color.
+        /// </summary>
+        int Color { get; }
+
+        #endregion
+    }
+}

+ 16 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/IGdiRegion.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Graphics Device Interface - Represents a Region Object.
+    /// </summary>
+    public interface IGdiRegion : IGdiObject
+    {
+        
+    }
+}

+ 22 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/PenEnum.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Enumeração de Pen
+    /// </summary>
+    public enum PenPSEnum
+    {
+         PS_SOLID = 0,
+         PS_DASH = 1,
+         PS_DOT = 2,
+         PS_DASHDOT = 3,
+         PS_DASHDOTDOT = 4,
+         PS_NULL = 5,
+         PS_INSIDEFRAME = 6,
+    }
+}

+ 90 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/Point.cs

@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Represents a point (x,y).
+    /// </summary>
+    public class Point
+    {
+        #region Properties
+
+        /// <summary>
+        /// Point X
+        /// </summary>
+        public int X { get; set; }
+
+        /// <summary>
+        /// Point Y
+        /// </summary>
+        public int Y { get; set; }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        public Point(int x, int y)
+        {
+            X = x;
+            Y = y;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            int prime = 31;
+            int result = 1;
+            result = prime * result + X;
+            result = prime * result + Y;
+            return result;
+        }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj)
+        {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (typeof(Point) != obj.GetType())
+                return false;
+            Point other = (Point)obj;
+            if (X != other.X)
+                return false;
+            if (Y != other.Y)
+                return false;
+            return true;
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return "Point [x=" + X + ", y=" + Y + "]";
+        }
+
+        #endregion
+    }
+}

+ 19 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/RegionEnum.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Region Enum
+    /// </summary>
+    public enum RegionEnum
+    {
+        NULLREGION = 1,
+        SIMPLEREGION = 2,
+        COMPLEXREGION = 3,
+        ERROR = 0,
+    }
+}

+ 90 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Gdi/Size.cs

@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Gdi
+{
+    /// <summary>
+    /// Represents a size object.
+    /// </summary>
+    public class Size
+    {
+        #region Properties
+
+        /// <summary>
+        /// Width of the object.
+        /// </summary>
+        public int Width { get; set; }
+
+        /// <summary>
+        /// Height of the object.
+        /// </summary>
+        public int Height { get; set; }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        public Size(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+
+        #endregion 
+
+        #region Public Methods
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            int prime = 31;
+            int result = 1;
+            result = prime * result + Height;
+            result = prime * result + Width;
+            return result;
+        }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj)
+        {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (typeof(Size) != obj.GetType())
+                return false;
+            Size other = (Size)obj;
+            if (Height != other.Height)
+                return false;
+            if (Width != other.Width)
+                return false;
+            return true;
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return "Size [width=" + Width + ", height=" + Height + "]";
+        }
+
+        #endregion
+    }
+}

+ 226 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/IO/DataInput.cs

@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.IO
+{
+    /// <summary>
+    /// Read input stream of wmf file.
+    /// </summary>
+    public class DataInput
+    {
+        #region Local Variables
+
+        private System.IO.Stream _inputStream;
+        private bool _isLittleEndian = BitConverter.IsLittleEndian;
+	
+	    private byte[] _buf = new byte[4];
+	    private int _count = 0;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Represents the count of bytes read
+        /// </summary>
+        public int Count
+        {
+            get
+            {
+                return _count;
+            }
+            set
+            {
+                _count = value;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// Create a DataInput instance using native order.
+        /// </summary>
+        /// <param name="inputStream"></param>
+	    public DataInput(System.IO.Stream inputStream) 
+        {
+            _isLittleEndian = BitConverter.IsLittleEndian;
+		    _inputStream = inputStream;
+	    }
+
+	    /// <summary>
+	    /// Default contructor.
+        /// Create a DataInput instance receving the order.
+	    /// </summary>
+	    /// <param name="inputStream"></param>
+	    /// <param name="endian"></param>
+	    public DataInput(System.IO.Stream inputStream, bool endian) 
+        {
+		    _inputStream = inputStream;
+            _isLittleEndian = endian;
+	    }
+
+        #endregion 
+
+        #region Public Methods
+
+        /// <summary>
+        /// Reads the next one byte of this input stream as a signed 8-bit integer.
+	    /// </summary>
+	    /// <returns></returns>
+	    public int ReadByte() 
+        {
+		    if (_inputStream.Read(_buf, 0, 1) == 1) 
+            {
+			    _count += 1;
+			    return (0xff & _buf[0]);
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+
+	   /// <summary>
+        /// Reads the next two bytes of this input stream as a signed 16-bit integer.
+	   /// </summary>
+	   /// <returns></returns>
+	    public int ReadInt16()  
+        {
+		    if (_inputStream.Read(_buf, 0, 2) == 2) 
+            {
+			    short value = 0;
+			    if (_isLittleEndian==false) 
+                {
+				    value |= (short)(0xff & _buf[1]);
+				    value |= (short)((0xff & _buf[0]) << 8);
+			    } 
+                else 
+                { 
+				    value |= (short)(0xff & _buf[0]);
+				    value |= (short)((0xff & _buf[1]) << 8);
+			    }
+			    _count += 2;
+			    return value;
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+
+        /// <summary>
+        /// Reads the next four bytes of this input stream as a signed 32-bit integer.
+        /// </summary>
+        /// <returns></returns>
+	    public int ReadInt32() 
+        {
+		    if (_inputStream.Read(_buf, 0, 4) == 4) 
+            {
+			    int value = 0;
+			    if (_isLittleEndian==false) 
+                {
+				    value |= (0xff & _buf[3]);
+				    value |= (0xff & _buf[2]) << 8;
+				    value |= (0xff & _buf[1]) << 16;
+				    value |= (0xff & _buf[0]) << 24;
+			    } 
+                else 
+                {
+				    value |= (0xff & _buf[0]);
+				    value |= (0xff & _buf[1]) << 8;
+				    value |= (0xff & _buf[2]) << 16;
+				    value |= (0xff & _buf[3]) << 24;
+			    }
+			    _count += 4;
+			    return value;
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+
+	    /// <summary>
+        /// Reads the next two bytes of this input stream as a unsigned 16-bit integer.
+	    /// </summary>
+	    /// <returns></returns>
+	    public int ReadUint16() 
+        {
+		    if (_inputStream.Read(_buf, 0, 2) == 2) 
+            {
+			    int value = 0;
+			    if (_isLittleEndian==false) 
+                {
+				    value |= (0xff & _buf[1]);
+				    value |= (0xff & _buf[0]) << 8;
+			    } else {
+				    value |= (0xff & _buf[0]);
+				    value |= (0xff & _buf[1]) << 8;
+			    }
+			    _count += 2;
+			    return value;
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+	    
+        /// <summary>
+        /// Reads the next four bytes of this input stream as a unsigned 32-bit integer.
+        /// </summary>
+        /// <returns></returns>
+	    public long ReadUint32()  
+        {
+		    if (_inputStream.Read(_buf, 0, 4) == 4) 
+            {
+			    long value = 0;
+			    if (_isLittleEndian ==false) 
+                {
+				    value |= (0xff & _buf[3]);
+				    value |= (0xff & _buf[2]) << 8;
+				    value |= (0xff & _buf[1]) << 16;
+				    value |= (0xff & _buf[0]) << 24;
+			    } 
+                else 
+                {
+				    value |= (0xff & _buf[0]);
+				    value |= (0xff & _buf[1]) << 8;
+				    value |= (0xff & _buf[2]) << 16;
+				    value |= (0xff & _buf[3]) << 24;
+			    }
+			    _count += 4;
+			    return value;
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+
+        /// <summary>
+        /// Reads the next n bytes.
+        /// </summary>
+        /// <param name="n"></param>
+        /// <returns></returns>
+	    public byte[] ReadBytes(int n) 
+        {
+		    byte[] array = new byte[n];
+		    if (_inputStream.Read(array,0,array.Length) == n) 
+            {
+			    _count += n;
+			    return array;
+		    }
+		    throw new System.IO.EndOfStreamException();
+	    }
+
+	
+        /// <summary>
+        /// Close the input stream.
+        /// </summary>
+	    public void Close() 
+        {
+		    try 
+            {
+			    _inputStream.Close();
+		    } 
+            catch(System.IO.IOException ex) 
+            {
+                Console.Write(ex.Message);
+		    }
+        }
+
+        #endregion 
+    }
+}

+ 132 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/Properties.cs

@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    public class Properties : Dictionary<string, string>
+    {
+        public void load()
+        {
+            var props = new Properties();
+            props.Add("alternative-font.\uff2d\uff33\u0020\u30b4\u30b7\u30c3\u30af", "MS Gothic");
+            props.Add("alternative-font.\uff2d\uff33\u0020\uff30\u30b4\u30b7\u30c3\u30af", "MS PGothic");
+            props.Add("alternative-font.\uff2d\uff33\u0020\u660e\u671d", "MS Mincho");
+            props.Add("alternative-font.\uff2d\uff33\u0020\uff30\u660e\u671d", "MS PMincho");
+            props.Add("alternative-font.\u30E1\u30A4\u30EA\u30AA", "Meiryo");
+
+            props.Add("font-emheight.Andalus", "0.655150351887396");
+            props.Add("font-emheight.Angsana\u0020New", "0.740285559371046");
+            props.Add("font-emheight.AngsanaUPC", "0.740285559371046");
+            props.Add("font-emheight.Aparajita", "0.846280991735537");
+            props.Add("font-emheight.Arabic\u0020Typesetting", "0.877463581833762");
+            props.Add("font-emheight.Arial", "0.895104895104895");
+            props.Add("font-emheight.Arial\u0020Black", "0.709141274238227");
+            props.Add("font-emheight.Browallia\u0020New", "0.800312622118015");
+            props.Add("font-emheight.BrowalliaUPC", "0.800312622118015");
+            props.Add("font-emheight.Calibri", "0.8192");
+            props.Add("font-emheight.Cambria", "0.852977925864223");
+            props.Add("font-emheight.Cambria\u0020Math", "0.179208960448022");
+            props.Add("font-emheight.Comic\u0020Sans\u0020MS", "0.717589348283111");
+            props.Add("font-emheight.Consolas", "0.854045037531276");
+            props.Add("font-emheight.Constantia", "0.8192");
+            props.Add("font-emheight.Corbel", "0.8192");
+            props.Add("font-emheight.Cordia\u0020New", "0.721380767876013");
+            props.Add("font-emheight.CordiaUPC", "0.721380767876013");
+            props.Add("font-emheight.Courier\u0020New", "0.882758620689655");
+            props.Add("font-emheight.DaunPenh", "0.745540589734256");
+            props.Add("font-emheight.David", "1.01637717121588");
+            props.Add("font-emheight.DejaVu\u0020Sans", "0.859060402684564");
+            props.Add("font-emheight.DejaVu\u0020Sans\u0020Mono", "0.859060402684564");
+            props.Add("font-emheight.DejaVu\u0020Serif", "0.859060402684564");
+            props.Add("font-emheight.DilleniaUPC", "0.766323666978485");
+            props.Add("font-emheight.DokChampa", "0.516259137887572");
+            props.Add("font-emheight.Dutch801\u0020SWC", "0.844187963726298");
+            props.Add("font-emheight.Ebrima", "0.751835535976505");
+            props.Add("font-emheight.Estrangelo\u0020Edessa", "0.894323144104804");
+            props.Add("font-emheight.EucrosiaUPC", "0.816912644595134");
+            props.Add("font-emheight.Euphemia", "0.761338289962825");
+            props.Add("font-emheight.Franklin\u0020Gothic\u0020Medium", "0.881998277347115");
+            props.Add("font-emheight.FrankRuehl", "1.07563025210084");
+            props.Add("font-emheight.FreesiaUPC", "0.834046019140705");
+            props.Add("font-emheight.Gabriola", "0.543236074270557");
+            props.Add("font-emheight.Gautami", "0.575928008998875");
+            props.Add("font-emheight.Gentium\u0020Basic", "0.864135021097046");
+            props.Add("font-emheight.Gentium\u0020Book\u0020Basic", "0.864135021097046");
+            props.Add("font-emheight.Georgia", "0.880103137086377");
+            props.Add("font-emheight.Gisha", "0.853333333333333");
+            props.Add("font-emheight.Impact", "0.819855884707766");
+            props.Add("font-emheight.IrisUPC", "0.790580968924918");
+            props.Add("font-emheight.Iskoola\u0020Pota", "0.880860215053763");
+            props.Add("font-emheight.JasmineUPC", "0.945958429561201");
+            props.Add("font-emheight.Kalinga", "0.673684210526316");
+            props.Add("font-emheight.Kartika", "0.701129750085587");
+            props.Add("font-emheight.Khmer\u0020UI", "0.883520276100086");
+            props.Add("font-emheight.KodchiangUPC", "1.0173869846001");
+            props.Add("font-emheight.Kokila", "0.867429055484964");
+            props.Add("font-emheight.Lao\u0020UI", "0.751835535976505");
+            props.Add("font-emheight.Latha", "0.728825622775801");
+            props.Add("font-emheight.Leelawadee", "0.836259697835851");
+            props.Add("font-emheight.Levenim\u0020MT", "0.757676655567888");
+            props.Add("font-emheight.Liberation\u0020Sans\u0020Narrow", "0.883139284174213");
+            props.Add("font-emheight.LilyUPC", "1.04944914168588");
+            props.Add("font-emheight.Lingoes\u0020Unicode", "0.746355685131195");
+            props.Add("font-emheight.Linux\u0020Biolinum\u0020G", "0.877087794432548");
+            props.Add("font-emheight.Linux\u0020Libertine\u0020G", "0.877087794432548");
+            props.Add("font-emheight.Lucida\u0020Sans\u0020Unicode", "0.650778519224658");
+            props.Add("font-emheight.Malgun\u0020Gothic", "0.751835535976505");
+            props.Add("font-emheight.Mangal", "0.595348837209302");
+            props.Add("font-emheight.Meiryo\u0020UI", "0.787389465590158");
+            props.Add("font-emheight.Microsoft\u0020JhengHei", "0.751835535976505");
+            props.Add("font-emheight.Microsoft\u0020New\u0020Tai\u0020Lue", "0.764464352370287");
+            props.Add("font-emheight.Microsoft\u0020PhagsPa", "0.781679389312977");
+            props.Add("font-emheight.Microsoft\u0020Sans\u0020Serif", "0.883520276100086");
+            props.Add("font-emheight.Microsoft\u0020Tai\u0020Le", "0.786482334869432");
+            props.Add("font-emheight.Microsoft\u0020YaHei", "0.757676655567888");
+            props.Add("font-emheight.Microsoft\u0020Yi\u0020Baiti", "0.999024390243902");
+            props.Add("font-emheight.Miriam\u0020Fixed", "1.01285855588526");
+            props.Add("font-emheight.Miriam", "0.99514091350826");
+            props.Add("font-emheight.Mongolian\u0020Baiti", "0.940312213039486");
+            props.Add("font-emheight.MoolBoran", "0.745540589734256");
+            props.Add("font-emheight.MV\u0020Boli", "0.620606060606061");
+            props.Add("font-emheight.Narkisim", "1.0123578843302");
+            props.Add("font-emheight.Nyala", "0.957009345794393");
+            props.Add("font-emheight.Palatino\u0020Linotype", "0.741223307998552");
+            props.Add("font-emheight.Plantagenet\u0020Cherokee", "0.787995382839554");
+            props.Add("font-emheight.Raavi", "0.602352941176471");
+            props.Add("font-emheight.Rod", "1.01941264310602");
+            props.Add("font-emheight.Sakkal\u0020Majalla", "0.716083916083916");
+            props.Add("font-emheight.Segoe\u0020Print", "0.573830204539087");
+            props.Add("font-emheight.Segoe\u0020Script", "0.631319358816276");
+            props.Add("font-emheight.Segoe\u0020UI", "0.751835535976505");
+            props.Add("font-emheight.Segoe\u0020UI\u0020Symbol", "0.751835535976505");
+            props.Add("font-emheight.Shonar\u0020Bangla", "0.772538664654847");
+            props.Add("font-emheight.Shruti", "0.594830090037758");
+            props.Add("font-emheight.Simplified\u0020Arabic", "0.603240058910162");
+            props.Add("font-emheight.Simplified\u0020Arabic\u0020Fixed", "0.915921288014311");
+            props.Add("font-emheight.Swiss742\u0020SWC", "0.803452334248725");
+            props.Add("font-emheight.Sylfaen", "0.759362254356693");
+            props.Add("font-emheight.Symbol", "0.816261458748505");
+            props.Add("font-emheight.Tahoma", "0.828478964401295");
+            props.Add("font-emheight.Times\u0020New\u0020Roman", "0.90299823633157");
+            props.Add("font-emheight.Traditional\u0020Arabic", "0.66906239790918");
+            props.Add("font-emheight.Trebuchet\u0020MS", "0.861227922624054");
+            props.Add("font-emheight.Tunga", "0.602352941176471");
+            props.Add("font-emheight.Utsaah", "0.893542757417103");
+            props.Add("font-emheight.Vani", "0.593795302986373");
+            props.Add("font-emheight.Verdana", "0.822820409803134");
+            props.Add("font-emheight.Vijaya", "0.993210475266731");
+            props.Add("font-emheight.Vrinda", "0.732737030411449");
+            props.Add("font-emheight.Wingdings", "0.901011878574571");
+            props.Add("font-emheight.Meiryo", "0.666666666666667");
+
+            props.Add("font-emheight.Academy\u0020Engraved\u0020LET", "0.870748299319728");
+            props.Add("font-emheight.Alma\u0020Iso", "0.997384481255449");
+            props.Add("font-emheight.Alma\u0020Large", "1.57250859106529");
+            props.Add("font-emheight.Alma\u0020Symbol", "0.997384481255449");
+            props.Add("font-emheight.AVGmdBU", "1.00986193293886");
+        }
+    }
+}

+ 271 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgBrush.cs

@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents a Brush SVG object.
+    /// </summary>
+    public class SvgBrush : SvgObject, Gdi.IGdiBrush
+    {
+        #region Local Variables 
+
+        private int _style;
+        private int _color;
+        private int _hatch;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object style.
+        /// </summary>
+        public int Style
+        {
+            get
+            {
+                return _style;
+            }
+        }
+
+        /// <summary>
+        /// Object color.
+        /// </summary>
+        public int Color
+        {
+            get
+            {
+                return _color;
+            }
+        }
+
+        /// <summary>
+        /// Object hatch.
+        /// </summary>
+        public int Hatch
+        {
+            get
+            {
+                return _hatch;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="style"></param>
+        /// <param name="color"></param>
+        /// <param name="hatch"></param>
+        public SvgBrush(
+            SvgGdi gdi,
+            int style,
+            int color,
+            int hatch)
+            : base(gdi)
+        {
+            _style = style;
+            _color = color;
+            _hatch = hatch;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Create element FillPatern.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public System.Xml.XmlElement CreateFillPattern(string id)
+        {
+            System.Xml.XmlElement pattern = GDI.Document.CreateElement("pattern");
+
+            if (_style == (int)Gdi.BrushBSEnum.BS_HATCHED)
+            {
+                pattern.SetAttribute("id", id);
+                pattern.SetAttribute("patternUnits", "userSpaceOnUse");
+                pattern.SetAttribute("x", "" + ToRealSize(0));
+                pattern.SetAttribute("y", "" + ToRealSize(0));
+                pattern.SetAttribute("width", "" + ToRealSize(8));
+                pattern.SetAttribute("height", "" + ToRealSize(8));
+                GDI.Document.AppendChild(pattern);
+
+                if (GDI.DC.BkMode == (int)Gdi.GdiEnum.OPAQUE)
+                {
+                    System.Xml.XmlElement rect = GDI.Document.CreateElement("rect");
+                    rect.SetAttribute("fill", ToColor(GDI.DC.BkColor));
+                    rect.SetAttribute("x", "" + ToRealSize(0));
+                    rect.SetAttribute("y", "" + ToRealSize(0));
+                    rect.SetAttribute("width", "" + ToRealSize(8));
+                    rect.SetAttribute("height", "" + ToRealSize(8));
+                    pattern.AppendChild(rect);
+                }
+
+                switch (_hatch)
+                {
+                    case (int)Gdi.BrushHSEnum.HS_HORIZONTAL:
+                        {
+                            System.Xml.XmlElement path = GDI.Document.CreateElement("line");
+                            path.SetAttribute("stroke", ToColor(_color));
+                            path.SetAttribute("x1", "" + ToRealSize(0));
+                            path.SetAttribute("y1", "" + ToRealSize(4));
+                            path.SetAttribute("x2", "" + ToRealSize(8));
+                            path.SetAttribute("y2", "" + ToRealSize(4));
+                            pattern.AppendChild(path);
+                        } break;
+                    case (int)Gdi.BrushHSEnum.HS_VERTICAL:
+                        {
+                            System.Xml.XmlElement path = GDI.Document.CreateElement("line");
+                            path.SetAttribute("stroke", ToColor(_color));
+                            path.SetAttribute("x1", "" + ToRealSize(4));
+                            path.SetAttribute("y1", "" + ToRealSize(0));
+                            path.SetAttribute("x2", "" + ToRealSize(4));
+                            path.SetAttribute("y2", "" + ToRealSize(8));
+                            pattern.AppendChild(path);
+                        } break;
+                    case (int)Gdi.BrushHSEnum.HS_FDIAGONAL:
+                        {
+                            System.Xml.XmlElement path = GDI.Document.CreateElement("line");
+                            path.SetAttribute("stroke", ToColor(_color));
+                            path.SetAttribute("x1", "" + ToRealSize(0));
+                            path.SetAttribute("y1", "" + ToRealSize(0));
+                            path.SetAttribute("x2", "" + ToRealSize(8));
+                            path.SetAttribute("y2", "" + ToRealSize(8));
+                            pattern.AppendChild(path);
+                        } break;
+                    case (int)Gdi.BrushHSEnum.HS_BDIAGONAL:
+                        {
+                            System.Xml.XmlElement path = GDI.Document.CreateElement("line");
+                            path.SetAttribute("stroke", ToColor(_color));
+                            path.SetAttribute("x1", "" + ToRealSize(0));
+                            path.SetAttribute("y1", "" + ToRealSize(8));
+                            path.SetAttribute("x2", "" + ToRealSize(8));
+                            path.SetAttribute("y2", "" + ToRealSize(0));
+                            pattern.AppendChild(path);
+                        } break;
+                    case (int)Gdi.BrushHSEnum.HS_CROSS:
+                        {
+                            System.Xml.XmlElement path1 = GDI.Document.CreateElement("line");
+                            path1.SetAttribute("stroke", ToColor(_color));
+                            path1.SetAttribute("x1", "" + ToRealSize(0));
+                            path1.SetAttribute("y1", "" + ToRealSize(4));
+                            path1.SetAttribute("x2", "" + ToRealSize(8));
+                            path1.SetAttribute("y2", "" + ToRealSize(4));
+                            pattern.AppendChild(path1);
+                            System.Xml.XmlElement path2 = GDI.Document.CreateElement("line");
+                            path2.SetAttribute("stroke", ToColor(_color));
+                            path2.SetAttribute("x1", "" + ToRealSize(4));
+                            path2.SetAttribute("y1", "" + ToRealSize(0));
+                            path2.SetAttribute("x2", "" + ToRealSize(4));
+                            path2.SetAttribute("y2", "" + ToRealSize(8));
+                            pattern.AppendChild(path2);
+                        } break;
+                    case (int)Gdi.BrushHSEnum.HS_DIAGCROSS:
+                        {
+                            System.Xml.XmlElement path1 = GDI.Document.CreateElement("line");
+                            path1.SetAttribute("stroke", ToColor(_color));
+                            path1.SetAttribute("x1", "" + ToRealSize(0));
+                            path1.SetAttribute("y1", "" + ToRealSize(0));
+                            path1.SetAttribute("x2", "" + ToRealSize(8));
+                            path1.SetAttribute("y2", "" + ToRealSize(8));
+                            pattern.AppendChild(path1);
+                            System.Xml.XmlElement path2 = GDI.Document.CreateElement("line");
+                            path2.SetAttribute("stroke", ToColor(_color));
+                            path2.SetAttribute("x1", "" + ToRealSize(0));
+                            path2.SetAttribute("y1", "" + ToRealSize(8));
+                            path2.SetAttribute("x2", "" + ToRealSize(8));
+                            path2.SetAttribute("y2", "" + ToRealSize(0));
+                            pattern.AppendChild(path2);
+                        } break;
+                }
+            }
+
+            return pattern;
+        }
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            int PRIME = 31;
+            int result = 1;
+            result = PRIME * result + _color;
+            result = PRIME * result + _hatch;
+            result = PRIME * result + _style;
+            return result;
+        }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj)
+        {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (typeof(SvgBrush) != obj.GetType())
+                return false;
+            SvgBrush other = (SvgBrush)obj;
+            if (_color != other._color)
+                return false;
+            if (_hatch != other._hatch)
+                return false;
+            if (_style != other._style)
+                return false;
+            return true;
+        }
+
+        /// <summary>
+        /// Create element inner text.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public System.Xml.XmlText CreateTextNode(string id)
+        {
+            return GDI.Document.CreateTextNode("." + id + " { " + ToString() + " }\r\n");
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+
+            // fill
+            switch (_style)
+            {
+                case (int)Gdi.BrushBSEnum.BS_SOLID:
+                    buffer.Append("fill: ").Append(ToColor(_color)).Append("; ");
+                    break;
+                case (int)Gdi.BrushBSEnum.BS_HATCHED:
+                    break;
+                default:
+                    buffer.Append("fill: none; ");
+                    break;
+            }
+
+            if (buffer.Length > 0)
+                buffer.Length = buffer.Length - 1;
+            return buffer.ToString();
+        }
+
+        #endregion
+    }
+}

+ 976 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgDc.cs

@@ -0,0 +1,976 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents SVG document.
+    /// </summary>
+    public class SvgDc : ICloneable
+    {
+
+        #region Local Variables
+
+        private SvgGdi _gdi;
+
+        private int _dpi = 1440;
+
+        // window
+        private int _wx = 0;
+        private int _wy = 0;
+        private int _ww = 0;
+        private int _wh = 0;
+
+        // window offset
+        private int _wox = 0;
+        private int _woy = 0;
+
+        // window scale
+        private double _wsx = 1.0;
+        private double _wsy = 1.0;
+
+        // mapping scale
+        private double _mx = 1.0;
+        private double _my = 1.0;
+
+        // viewport
+        private int _vx = 0;
+        private int _vy = 0;
+        private int _vw = 0;
+        private int _vh = 0;
+
+        // viewport offset
+        private int _vox = 0;
+        private int _voy = 0;
+
+        // viewport scale
+        private double _vsx = 1.0;
+        private double _vsy = 1.0;
+
+        // current location
+        private int _cx = 0;
+        private int _cy = 0;
+
+        // clip offset
+        private int _cox = 0;
+        private int _coy = 0;
+
+        private int _mapMode = (int)Gdi.GdiEnum.MM_TEXT;
+        private int _bkColor = 0x00FFFFFF;
+        private int _bkMode = (int)Gdi.GdiEnum.OPAQUE;
+        private int _textColor = 0x00000000;
+        private int _textSpace = 0;
+        private int _textAlign = (int)Gdi.GdiEnum.TA_TOP | (int)Gdi.GdiEnum.TA_LEFT;
+        private int _textDx = 0;
+        private int _polyFillMode = (int)Gdi.GdiEnum.ALTERNATE;
+        private int _relAbsMode = 0;
+        private int _rop2Mode = (int)Gdi.GdiEnum.R2_COPYPEN;
+        private int _stretchBltMode = (int)Gdi.GdiEnum.STRETCH_ANDSCANS;
+        private long _layout = 0;
+        private long _mapperFlags = 0;
+
+        private SvgBrush _brush = null;
+        private SvgFont _font = null;
+        private SvgPen _pen = null;
+
+        private System.Xml.XmlElement _mask = null;
+
+        #endregion 
+
+        #region Properties
+
+        /// <summary>
+        /// Background color.
+        /// </summary>
+        public int BkColor
+        {
+            get
+            {
+                return _bkColor;
+            }
+            set
+            {
+                _bkColor = value;
+            }
+        }
+
+        /// <summary>
+        /// Background mode.
+        /// </summary>
+        public int BkMode
+        {
+            get
+            {
+                return _bkMode;
+            }
+            set
+            {
+                _bkColor = value;
+            }
+        }
+
+        /// <summary>
+        /// Text color.
+        /// </summary>
+        public int TextColor
+        {
+            get
+            {
+                return _textColor;
+            }
+            set
+            {
+                _textColor = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int PolyFillMode
+        {
+            get
+            {
+                return _polyFillMode;
+
+            }
+            set
+            {
+                _polyFillMode = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int RelAbs
+        {
+            get
+            {
+                return _relAbsMode;
+            }
+            set
+            {
+                _relAbsMode = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int ROP2
+        {
+            get
+            {
+                return _rop2Mode;
+            }
+            set
+            {
+                _rop2Mode = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int StretchBltMode
+        {
+            get
+            {
+                return _stretchBltMode;
+            }
+            set
+            {
+                _stretchBltMode = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TextSpace
+        {
+            get
+            {
+                return _textSpace;
+            }
+            set
+            {
+                _textSpace = value;
+            }
+        }
+
+        /// <summary>
+        /// Text align.
+        /// </summary>
+        public int TextAlign
+        {
+            get
+            {
+                return _textAlign;
+            }
+            set
+            {
+                _textAlign = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int TextCharacterExtra
+        {
+            get
+            {
+                return _textDx;
+            }
+            set
+            {
+                _textDx = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public long Layout
+        {
+            get
+            {
+                return _layout;
+            }
+            set
+            {
+                _layout = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public long MapperFlags
+        {
+            get
+            {
+                return _mapperFlags;
+            }
+            set
+            {
+                _mapperFlags = value;
+            }
+        }
+
+        /// <summary>
+        /// Brush instance.
+        /// </summary>
+        public SvgBrush Brush
+        {
+            get
+            {
+                return _brush;
+            }
+            set
+            {
+                _brush = value;
+            }
+        }
+
+        /// <summary>
+        /// Font instance.
+        /// </summary>
+        public SvgFont Font
+        {
+            get
+            {
+                return _font;
+            }
+            set
+            {
+                _font = value;
+            }
+        }
+
+        /// <summary>
+        /// Pen instance.
+        /// </summary>
+        public SvgPen Pen
+        {
+            get
+            {
+                return _pen;
+            }
+            set
+            {
+                _pen = value;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public System.Xml.XmlElement Mask
+        {
+            get
+            {
+                return _mask;
+            }
+            set
+            {
+                _mask = value;
+            }
+        }
+
+        /// <summary>
+        /// Current X Point.
+        /// </summary>
+        public int CurrentX
+        {
+            get
+            {
+                return _cx;
+            }
+        }
+
+        /// <summary>
+        /// Current Y Point.
+        /// </summary>
+        public int CurrentY
+        {
+            get
+            {
+                return _cy;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OffsetClipX
+        {
+            get
+            {
+                return _cox;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int OffsetClipY
+        {
+            get
+            {
+                return _coy;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int MapMode
+        {
+            get
+            {
+                return _mapMode;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int Dpi
+        {
+            get
+            {
+                return _dpi;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int WindowX
+        {
+            get
+            {
+                return _wx;
+            }
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int WindowY
+        {
+            get
+            {
+                return _wy;
+            }
+        }
+
+        /// <summary>
+        /// Represents window width.
+        /// </summary>
+        public int WindowWidth
+        {
+            get
+            {
+                return _ww;
+            }
+        }
+
+        /// <summary>
+        /// Represents window height.
+        /// </summary>
+        public int WindowHeight
+        {
+            get
+            {
+                return _wh;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default Constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        public SvgDc(SvgGdi gdi)
+        {
+            _gdi = gdi;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Specifies which window point maps to the viewport origin (0,0).
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        public void SetWindowOrgEx(int x, int y, WMFConverter.Gdi.Point old)
+        {
+            if (old != null)
+            {
+                old.X = _wx;
+                old.Y = _wy;
+            }
+            _wx = x;
+            _wy = y;
+        }
+
+        /// <summary>
+        /// Sets the horizontal and vertical extents of the window for a device context by using the specified values.
+        /// </summary>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        /// <param name="old"></param>
+        public void SetWindowExtEx(int width, int height, WMFConverter.Gdi.Size old)
+        {
+            if (old != null)
+            {
+                old.Width = _ww;
+                old.Height = _wh;
+            }
+            _ww = width;
+            _wh = height;
+        }
+
+        /// <summary>
+        /// Modifies the window origin for a device context using the specified horizontal and vertical offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        public void OffSetWindowOrgEx(int x, int y, WMFConverter.Gdi.Point old)
+        {
+            if (old != null)
+            {
+                old.X = _wox;
+                old.Y = _woy;
+            }
+            _wox += x;
+            _woy += y;
+        }
+
+        /// <summary>
+        /// Modifies the window for a device context using the ratios formed by the specified multiplicands and divisors.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="xd"></param>
+        /// <param name="y"></param>
+        /// <param name="yd"></param>
+        /// <param name="old"></param>
+        public void ScaleWindowExtEx(int x, int xd, int y, int yd, WMFConverter.Gdi.Size old)
+        {
+            // TODO
+            _wsx = (_wsx * x) / xd;
+            _wsy = (_wsy * y) / yd;
+        }
+
+        /// <summary>
+        /// Specifies which device point maps to the window origin (0,0).
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        public void SetViewportOrgEx(int x, int y, WMFConverter.Gdi.Point old)
+        {
+            if (old != null)
+            {
+                old.X = _vx;
+                old.Y = _vy;
+            }
+            _vx = x;
+            _vy = y;
+        }
+
+        /// <summary>
+        /// Sets the horizontal and vertical extents of the viewport for a device context by using the specified values.
+        /// </summary>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        /// <param name="old"></param>
+        public void SetViewportExtEx(int width, int height, WMFConverter.Gdi.Size old)
+        {
+            if (old != null)
+            {
+                old.Width = _vw;
+                old.Height = _vh;
+            }
+            _vw = width;
+            _vh = height;
+        }
+
+        /// <summary>
+        /// Modifies the viewport origin for a device context using the specified horizontal and vertical offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        public void OffSetViewportOrgEx(int x, int y, WMFConverter.Gdi.Point old)
+        {
+            if (old != null)
+            {
+                old.X = _vox;
+                old.Y = _voy;
+            }
+            _vox = x;
+            _voy = y;
+        }
+
+        /// <summary>
+        /// Modifies the viewport for a device context using the ratios formed by the specified multiplicands and divisors.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="xd"></param>
+        /// <param name="y"></param>
+        /// <param name="yd"></param>
+        /// <param name="old"></param>
+        public void ScaleViewportExtEx(int x, int xd, int y, int yd, WMFConverter.Gdi.Size old)
+        {
+            // TODO
+            _vsx = (_vsx * x) / xd;
+            _vsy = (_vsy * y) / yd;
+        }
+
+        /// <summary>
+        /// Moves the clipping region of a device context by the specified offsets.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        public void OffSetClipRgn(int x, int y)
+        {
+            _cox = x;
+            _coy = y;
+        }
+
+        /// <summary>
+        /// Sets the mapping mode of the specified device context. 
+        /// The mapping mode defines the unit of measure used to transform page-space units into device-space units, and also defines the orientation of the device's x and y axes.
+        /// </summary>
+        /// <param name="mode"></param>
+        public void SetMapMode(int mode)
+        {
+            _mapMode = mode;
+            switch (mode)
+            {
+                case (int)Gdi.GdiEnum.MM_HIENGLISH:
+                    _mx = 0.09;
+                    _my = -0.09;
+                    break;
+                case (int)Gdi.GdiEnum.MM_LOENGLISH:
+                    _mx = 0.9;
+                    _my = -0.9;
+                    break;
+                case (int)Gdi.GdiEnum.MM_HIMETRIC:
+                    _mx = 0.03543307;
+                    _my = -0.03543307;
+                    break;
+                case (int)Gdi.GdiEnum.MM_LOMETRIC:
+                    _mx = 0.3543307;
+                    _my = -0.3543307;
+                    break;
+                case (int)Gdi.GdiEnum.MM_TWIPS:
+                    _mx = 0.0625;
+                    _my = -0.0625;
+                    break;
+                default:
+                    _mx = 1.0;
+                    _my = 1.0;
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Updates the current position to the specified point and optionally returns the previous position.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <param name="old"></param>
+        public void MoveToEx(int x, int y, WMFConverter.Gdi.Point old)
+        {
+            if (old != null)
+            {
+                old.X = _cx;
+                old.Y = _cy;
+            }
+            _cx = x;
+            _cy = y;
+        }
+
+        /// <summary>
+        /// Return the absolute X position from x point.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <returns></returns>
+        public double ToAbsoluteX(double x)
+        {
+            // TODO Handle Viewport
+            return ((_ww >= 0) ? 1 : -1) * (_mx * x - (_wx + _wox)) / _wsx;
+        }
+
+        /// <summary>
+        /// Return the absolute Y position from y point.
+        /// </summary>
+        /// <param name="y"></param>
+        /// <returns></returns>
+        public double ToAbsoluteY(double y)
+        {
+            // TODO Handle Viewport
+            return ((_wh >= 0) ? 1 : -1) * (_my * y - (_wy + _woy)) / _wsy;
+        }
+
+        /// <summary>
+        /// Return the relative X position from x point.
+        /// </summary>
+        /// <param name="x"></param>
+        /// <returns></returns>
+        public double ToRelativeX(double x)
+        {
+            // TODO Handle Viewport
+            return ((_ww >= 0) ? 1 : -1) * (_mx * x) / _wsx;
+        }
+
+        /// <summary>
+        /// Return the relative Y position from y point.
+        /// </summary>
+        /// <param name="y"></param>
+        /// <returns></returns>
+        public double ToRelativeY(double y)
+        {
+            // TODO Handle Viewport
+            return ((_wh >= 0) ? 1 : -1) * (_my * y) / _wsy;
+        }
+
+        /// <summary>
+        /// Define Dpi value
+        /// </summary>
+        /// <param name="dpi"></param>
+        public void SetDpi(int dpi)
+        {
+            _dpi = (dpi > 0) ? dpi : 1440;
+        }
+
+        
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="rop"></param>
+        /// <returns></returns>
+        public string GetRopFilter(long rop)
+        {
+            string name = null;
+            System.Xml.XmlDocument doc = _gdi.Document;
+            
+            if (rop == (int)Gdi.GdiEnum.BLACKNESS)
+            {
+                name = "BLACKNESS_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "SourceGraphic");
+                    feColorMatrix.SetAttribute("values", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0");
+                    filter.AppendChild(feColorMatrix);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.NOTSRCERASE)
+            {
+                name = "NOTSRCERASE_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feComposite = doc.CreateElement("feComposite");
+                    feComposite.SetAttribute("in", "SourceGraphic");
+                    feComposite.SetAttribute("in2", "BackgroundImage");
+                    feComposite.SetAttribute("operator", "arithmetic");
+                    feComposite.SetAttribute("k1", "1");
+                    feComposite.SetAttribute("result", "result0");
+                    filter.AppendChild(feComposite);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("in", "result0");
+                    feColorMatrix.SetAttribute("values", "-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0");
+                    filter.AppendChild(feColorMatrix);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.NOTSRCCOPY)
+            {
+                name = "NOTSRCCOPY_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "SourceGraphic");
+                    feColorMatrix.SetAttribute("values", "-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0");
+                    filter.AppendChild(feColorMatrix);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.SRCERASE)
+            {
+                name = "SRCERASE_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "BackgroundImage");
+                    feColorMatrix.SetAttribute("values", "-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0");
+                    feColorMatrix.SetAttribute("result", "result0");
+                    filter.AppendChild(feColorMatrix);
+
+                    System.Xml.XmlElement feComposite = doc.CreateElement("feComposite");
+                    feComposite.SetAttribute("in", "SourceGraphic");
+                    feComposite.SetAttribute("in2", "result0");
+                    feComposite.SetAttribute("operator", "arithmetic");
+                    feComposite.SetAttribute("k2", "1");
+                    feComposite.SetAttribute("k3", "1");
+                    filter.AppendChild(feComposite);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.PATINVERT)
+            {
+                // TODO
+            }
+            else if (rop == (int)Gdi.GdiEnum.SRCINVERT)
+            {
+                // TODO
+            }
+            else if (rop == (int)Gdi.GdiEnum.DSTINVERT)
+            {
+                name = "DSTINVERT_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.SetIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "BackgroundImage");
+                    feColorMatrix.SetAttribute("values", "-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0");
+                    filter.AppendChild(feColorMatrix);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.SRCAND)
+            {
+                name = "SRCAND_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feComposite = doc.CreateElement("feComposite");
+                    feComposite.SetAttribute("in", "SourceGraphic");
+                    feComposite.SetAttribute("in2", "BackgroundImage");
+                    feComposite.SetAttribute("operator", "arithmetic");
+                    feComposite.SetAttribute("k1", "1");
+                    filter.AppendChild(feComposite);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.MERGEPAINT)
+            {
+                name = "MERGEPAINT_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "SourceGraphic");
+                    feColorMatrix.SetAttribute("values", "-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0");
+                    feColorMatrix.SetAttribute("result", "result0");
+                    filter.AppendChild(feColorMatrix);
+
+                    System.Xml.XmlElement feComposite = doc.CreateElement("feComposite");
+                    feComposite.SetAttribute("in", "result0");
+                    feComposite.SetAttribute("in2", "BackgroundImage");
+                    feComposite.SetAttribute("operator", "arithmetic");
+                    feComposite.SetAttribute("k1", "1");
+                    filter.AppendChild(feComposite);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.MERGECOPY)
+            {
+                // TODO
+            }
+            else if (rop == (int)Gdi.GdiEnum.SRCPAINT)
+            {
+                name = "SRCPAINT_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.setIdAttribute("id", true);
+
+                    System.Xml.XmlElement feComposite = doc.CreateElement("feComposite");
+                    feComposite.SetAttribute("in", "SourceGraphic");
+                    feComposite.SetAttribute("in2", "BackgroundImage");
+                    feComposite.SetAttribute("operator", "arithmetic");
+                    feComposite.SetAttribute("k2", "1");
+                    feComposite.SetAttribute("k3", "1");
+                    filter.AppendChild(feComposite);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+            else if (rop == (int)Gdi.GdiEnum.PATCOPY)
+            {
+                // TODO
+            }
+            else if (rop == (int)Gdi.GdiEnum.PATPAINT)
+            {
+                // TODO
+            }
+            else if (rop == (int)Gdi.GdiEnum.WHITENESS)
+            {
+                name = "WHITENESS_FILTER";
+                System.Xml.XmlElement filter = doc.GetElementById(name);
+                if (filter == null)
+                {
+                    filter = _gdi.Document.CreateElement("filter");
+                    filter.SetAttribute("id", name);
+                    //filter.SetIdAttribute("id", true);
+
+                    System.Xml.XmlElement feColorMatrix = doc.CreateElement("feColorMatrix");
+                    feColorMatrix.SetAttribute("type", "matrix");
+                    feColorMatrix.SetAttribute("in", "SourceGraphic");
+                    feColorMatrix.SetAttribute("values", "1 0 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0 1 0");
+                    filter.AppendChild(feColorMatrix);
+
+                    _gdi.DefsElement.AppendChild(filter);
+                }
+            }
+
+            if (name != null)
+            {
+                if (!doc.DocumentElement.HasAttribute("enable-background"))
+                {
+                    doc.DocumentElement.SetAttribute("enable-background", "new");
+                }
+                return "url(#" + name + ")";
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Clone this object.
+        /// </summary>
+        /// <returns></returns>
+        public object Clone()
+        {
+            return this.Clone();
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return "SvgDc [gdi=" + _gdi + ", dpi=" + _dpi + ", wx=" + _wx + ", wy="
+                    + _wy + ", ww=" + _ww + ", wh=" + _wh + ", wox=" + _wox + ", woy="
+                    + _woy + ", wsx=" + _wsx + ", wsy=" + _wsy + ", mx=" + _mx
+                    + ", my=" + _my + ", vx=" + _vx + ", vy=" + _vy + ", vw=" + _vw
+                    + ", vh=" + _vh + ", vox=" + _vox + ", voy=" + _voy + ", vsx="
+                    + _vsx + ", vsy=" + _vsy + ", cx=" + _cx + ", cy=" + _cy
+                    + ", mapMode=" + _mapMode + ", bkColor=" + _bkColor + ", bkMode="
+                    + _bkMode + ", textColor=" + _textColor + ", textSpace="
+                    + _textSpace + ", textAlign=" + _textAlign + ", textDx=" + _textDx
+                    + ", polyFillMode=" + _polyFillMode + ", relAbsMode="
+                    + _relAbsMode + ", rop2Mode=" + _rop2Mode + ", stretchBltMode="
+                    + _stretchBltMode + ", brush=" + _brush + ", font=" + _font
+                    + ", pen=" + _pen + "]";
+        }
+
+        #endregion
+    }
+}

+ 532 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgFont.cs

@@ -0,0 +1,532 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents a SVG Font object.
+    /// </summary>
+    public class SvgFont : SvgObject,Gdi.IGdiFont
+    {
+        #region Properties
+
+        /// <summary>
+        /// Object height.
+        /// </summary>
+        public int Height
+        {
+            get
+            {
+                return _height;
+            }
+        }
+
+        /// <summary>
+        /// Object width.
+        /// </summary>
+        public int Width
+        {
+            get
+            {
+                return _width;
+            }
+        }
+
+        /// <summary>
+        /// Object escapement.
+        /// </summary>
+        public int Escapement
+        {
+            get
+            {
+                return _escapement;
+            }
+        }
+
+        /// <summary>
+        /// Object orientation
+        /// </summary>
+        public int Orientation
+        {
+            get
+            {
+                return _orientation;
+            }
+        }
+
+        /// <summary>
+        /// Object weight.
+        /// </summary>
+        public int Weight
+        {
+            get
+            {
+                return _weight;
+            }
+        }
+
+        /// <summary>
+        /// Italic font.
+        /// </summary>
+        public bool IsItalic
+        {
+            get
+            {
+                return _italic;
+            }
+        }
+
+        /// <summary>
+        /// Underlined font.
+        /// </summary>
+        public bool IsUnderlined
+        {
+            get
+            {
+                return _underline;
+            }
+        }
+
+        /// <summary>
+        /// Striked font.
+        /// </summary>
+        public bool IsStrikedOut
+        {
+            get
+            {
+                return _strikeout;
+            }
+        }
+
+        /// <summary>
+        /// Object charset.
+        /// </summary>
+        public int Charset
+        {
+            get
+            {
+                return _charset;
+            }
+        }
+
+        /// <summary>
+        /// Object precision.
+        /// </summary>
+        public int OutPrecision
+        {
+            get
+            {
+                return _outPrecision;
+            }
+        }
+
+        /// <summary>
+        /// Object ClipPrecision.
+        /// </summary>
+        public int ClipPrecision
+        {
+            get
+            {
+                return _clipPrecision;
+            }
+        }
+
+        /// <summary>
+        /// Font quality.
+        /// </summary>
+        public int Quality
+        {
+            get
+            {
+                return _quality;
+            }
+        }
+
+        /// <summary>
+        /// Defines pitch and family.
+        /// </summary>
+        public int PitchAndFamily
+        {
+            get
+            {
+                return _pitchAndFamily;
+            }
+        }
+
+        /// <summary>
+        /// Specifies the font name.
+        /// </summary>
+        public string FaceName
+        {
+            get
+            {
+                return _faceName;
+            }
+        }
+
+        /// <summary>
+        /// Font language.
+        /// </summary>
+        public string Lang
+        {
+            get
+            {
+                return _lang;
+            }
+        }
+        
+        /// <summary>
+        /// Font size.
+        /// </summary>
+        public int FontSize
+        {
+            get
+            {
+                return Math.Abs((int)GDI.DC.ToRelativeY(_height * _heightMultiply));
+            }
+        }
+
+        #endregion
+
+        #region Local Variables
+
+        private int _height;
+        private int _width;
+        private int _escapement;
+        private int _orientation;
+        private int _weight;
+        private bool _italic;
+        private bool _underline;
+        private bool _strikeout;
+        private int _charset;
+        private int _outPrecision;
+        private int _clipPrecision;
+        private int _quality;
+        private int _pitchAndFamily;
+
+        private string _faceName;
+        private double _heightMultiply = 1.0;
+        private string _lang;
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default Constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="height"></param>
+        /// <param name="width"></param>
+        /// <param name="escapement"></param>
+        /// <param name="orientation"></param>
+        /// <param name="weight"></param>
+        /// <param name="italic"></param>
+        /// <param name="underline"></param>
+        /// <param name="strikeout"></param>
+        /// <param name="charset"></param>
+        /// <param name="outPrecision"></param>
+        /// <param name="clipPrecision"></param>
+        /// <param name="quality"></param>
+        /// <param name="pitchAndFamily"></param>
+        /// <param name="faceName"></param>
+        public SvgFont(
+            SvgGdi gdi,
+            int height,
+            int width,
+            int escapement,
+            int orientation,
+            int weight,
+            bool italic,
+            bool underline,
+            bool strikeout,
+            int charset,
+            int outPrecision,
+            int clipPrecision,
+            int quality,
+            int pitchAndFamily,
+            byte[] faceName)
+            : base (gdi)
+        {
+            _height = height;
+            _width = width;
+            _escapement = escapement;
+            _orientation = orientation;
+            _weight = weight;
+            _italic = italic;
+            _underline = underline;
+            _strikeout = strikeout;
+            _charset = charset;
+            _outPrecision = outPrecision;
+            _clipPrecision = clipPrecision;
+            _quality = quality;
+            _pitchAndFamily = pitchAndFamily;
+            _faceName = WMFConverter.Gdi.GdiUtils.ConvertString(faceName, charset);
+
+            // xml:lang
+            _lang = WMFConverter.Gdi.GdiUtils.GetLanguage(charset);
+
+            string emheight = gdi.GetProperty("font-emheight." + _faceName);
+            if (emheight == null)
+            {
+                string alter = gdi.GetProperty("alternative-font." + _faceName);
+                if (alter != null)
+                {
+                    emheight = gdi.GetProperty("font-emheight." + alter);
+                }
+            }
+
+            if (emheight != null)
+            {
+                _heightMultiply = Convert.ToDouble(emheight);
+            }
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="chars"></param>
+        /// <param name="dx"></param>
+        /// <returns></returns>
+        public int[] ValidateDx(byte[] chars, int[] dx)
+        {
+            if (dx == null || dx.Length == 0)
+            {
+                return null;
+            }
+
+            int[,] area = WMFConverter.Gdi.GdiUtils.GetFirstByteArea(_charset);
+            if (area == null)
+            {
+                return dx;
+            }
+
+            int n = 0;
+            bool skip = false;
+
+            for (int i = 0; i < chars.Length && i < dx.Length; i++)
+            {
+                int c = (0xFF & chars[i]);
+
+                if (skip)
+                {
+                    dx[n - 1] += dx[i];
+                    skip = false;
+                    continue;
+                }
+
+                for (int j = 0; j < area.Length; j++)
+                {
+                    if (area[j,0] <= c && c <= area[j,1])
+                    {
+                        skip = true;
+                        break;
+                    }
+                }
+
+                dx[n++] = dx[i];
+            }
+
+            int[] ndx = new int[n];
+            Array.Copy(dx, 0, ndx, 0, n);
+
+            return ndx;
+        }
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode() 
+        {
+		    int PRIME = 31;
+		    int result = 1;
+		    result = PRIME * result + _charset;
+		    result = PRIME * result + _clipPrecision;
+		    result = PRIME * result + _escapement;
+		    result = PRIME * result + ((_faceName == null) ? 0 : _faceName.GetHashCode());
+		    result = PRIME * result + _height;
+		    result = PRIME * result + (_italic ? 1231 : 1237);
+		    result = PRIME * result + _orientation;
+		    result = PRIME * result + _outPrecision;
+		    result = PRIME * result + _pitchAndFamily;
+		    result = PRIME * result + _quality;
+		    result = PRIME * result + (_strikeout ? 1231 : 1237);
+		    result = PRIME * result + (_underline ? 1231 : 1237);
+		    result = PRIME * result + _weight;
+		    result = PRIME * result + _width;
+		    return result;
+	    }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj) 
+        {
+		    if (this == obj)
+			    return true;
+		    if (obj == null)
+			    return false;
+		    if (typeof(SvgFont) != obj.GetType())
+			    return false;
+		    SvgFont other = (SvgFont) obj;
+		    if (_charset != other._charset)
+			    return false;
+		    if (_clipPrecision != other._clipPrecision)
+			    return false;
+		    if (_escapement != other._escapement)
+			    return false;
+		    if (_faceName == null) 
+            {
+			    if (other._faceName != null)
+				    return false;
+		    } 
+            else if (!_faceName.Equals(other._faceName))
+			    return false;
+		    if (_height != other._height)
+			    return false;
+		    if (_italic != other._italic)
+			    return false;
+		    if (_orientation != other._orientation)
+			    return false;
+		    if (_outPrecision != other._outPrecision)
+			    return false;
+		    if (_pitchAndFamily != other._pitchAndFamily)
+			    return false;
+		    if (_quality != other._quality)
+			    return false;
+		    if (_strikeout != other._strikeout)
+			    return false;
+		    if (_underline != other._underline)
+			    return false;
+		    if (_weight != other._weight)
+			    return false;
+		    if (_width != other._width)
+			    return false;
+		    return true;
+	    }
+
+        /// <summary>
+        /// Create inner text element.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public System.Xml.XmlText CreateTextNode(string id)
+        {
+            return GDI.Document.CreateTextNode("." + id + " { " + ToString() + " }\r\n");
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+
+            // font-style
+            if (_italic)
+                buffer.Append("font-style: italic; ");
+
+            // font-weight
+            if (_weight != (int)Gdi.FontFWEnum.FW_DONTCARE && _weight != (int)Gdi.FontFWEnum.FW_NORMAL)
+            {
+                if (_weight < 100)
+                    _weight = 100;
+                else if (_weight > 900)
+                    _weight = 900;
+                else
+                    _weight = (_weight / 100) * 100;
+
+                if (_weight == (int)Gdi.FontFWEnum.FW_BOLD)
+                    buffer.Append("font-weight: bold; ");
+                else
+                    buffer.Append("font-weight: " + _weight + "; ");
+            }
+
+            int fontSize = FontSize;
+            if (fontSize != 0) 
+                buffer.Append("font-size: ").Append(fontSize).Append("px; ");
+
+            // font-family
+            List<string> fontList = new List<string>();
+            if (_faceName.Length != 0)
+            {
+                string fontFamily = _faceName;
+                if (_faceName.ElementAt(0) == '@') fontFamily = _faceName.Substring(1);
+                fontList.Add(fontFamily);
+
+                string altfont = GDI.GetProperty("alternative-font." + fontFamily);
+                if (altfont != null && altfont.Length != 0)
+                {
+                    fontList.Add(altfont);
+                }
+            }
+
+            // int pitch = pitchAndFamily & 0x00000003;
+            int family = _pitchAndFamily & 0x000000F0;
+            switch (family)
+            {
+                case (int)Gdi.FontFFEnum.FF_DECORATIVE:
+                    fontList.Add("fantasy");
+                    break;
+                case (int)Gdi.FontFFEnum.FF_MODERN:
+                    fontList.Add("monospace");
+                    break;
+                case (int)Gdi.FontFFEnum.FF_ROMAN:
+                    fontList.Add("serif");
+                    break;
+                case (int)Gdi.FontFFEnum.FF_SCRIPT:
+                    fontList.Add("cursive");
+                    break;
+                case (int)Gdi.FontFFEnum.FF_SWISS:
+                    fontList.Add("sans-serif");
+                    break;
+            }
+
+            if (fontList != null)
+                if (fontList.Count()>0)
+                {
+                    buffer.Append("font-family:");
+                    for (int i = 0; i < fontList.Count(); i ++ )
+                    {
+                        string font = fontList[i];
+                        if (font.IndexOf(" ") != -1)
+                            buffer.Append(" \"" + font + "\"");
+                        else
+                            buffer.Append(" " + font);
+
+                        if (i < fontList.Count())
+                            buffer.Append(",");
+                    }
+                    buffer.Append("; ");
+                }
+
+            // text-decoration
+            if (_underline || _strikeout)
+            {
+                buffer.Append("text-decoration:");
+                if (_underline)
+                    buffer.Append(" underline");
+                if (_strikeout)
+                    buffer.Append(" overline");
+                buffer.Append("; ");
+            }
+
+            if (buffer.Length > 0) 
+                buffer.Length = buffer.Length - 1;
+            return buffer.ToString();
+        }
+
+        #endregion
+    }
+}

File diff suppressed because it is too large
+ 2292 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgGdi.cs


+ 77 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgObject.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - SVG base object.
+    /// </summary>
+    public abstract class SvgObject
+    {
+        #region Local Variables
+
+        private SvgGdi _gdi;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Gdi object instance.
+        /// </summary>
+        /// <returns></returns>
+        public SvgGdi GDI
+        {
+            get
+            {
+                return _gdi;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        public SvgObject(SvgGdi gdi)
+        {
+            _gdi = gdi;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Convert to real size.
+        /// </summary>
+        /// <param name="px"></param>
+        /// <returns></returns>
+        public int ToRealSize(int px)
+        {
+            return GDI.DC.Dpi * px / 90;
+        }
+
+        /// <summary>
+        /// Convert int color to rgb color.
+        /// </summary>
+        /// <param name="color"></param>
+        /// <returns></returns>
+        public static string ToColor(int color)
+        {
+            int b = (0x00FF0000 & color) >> 16;
+            int g = (0x0000FF00 & color) >> 8;
+            int r = (0x000000FF & color);
+
+            return "rgb(" + r + "," + g + "," + b + ")";
+        }
+
+        #endregion
+    }
+}

+ 67 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPalette.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents a SVG Palette object.
+    /// </summary>
+    public class SvgPalette: SvgObject, Gdi.IGdiPalette
+    {
+        #region Local Variables
+
+        private int _version;
+        private int[] _entries;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object version.
+        /// </summary>
+        public int Version
+        {
+            get
+            {
+                return _version;
+            }
+        }
+
+        /// <summary>
+        /// Object entries.
+        /// </summary>
+        public int[] Entries
+        {
+            get
+            {
+                return _entries;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="version"></param>
+        /// <param name="entries"></param>
+        public SvgPalette(
+            SvgGdi gdi,
+            int version,
+            int[] entries)
+            :base(gdi)
+        {
+            _version = version;
+            _entries = entries;
+        }
+
+        #endregion
+    }
+}

+ 50 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPatternBrush.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents a SVG Pattern Brush object.
+    /// </summary>
+    public class SvgPatternBrush : SvgObject, Gdi.IGdiPatternBrush
+    {
+        #region Local Variables
+
+        private byte[] _bmp;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object patterns
+        /// </summary>
+        public byte[] Pattern
+        {
+            get
+            {
+                return _bmp;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="bmp"></param>
+        public SvgPatternBrush(SvgGdi gdi, byte[] bmp)
+            : base(gdi)
+        {
+            _bmp = bmp;
+        }
+
+        #endregion
+    }
+}

+ 205 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgPen.cs

@@ -0,0 +1,205 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents a Pen object.
+    /// </summary>
+    public class SvgPen: SvgObject,Gdi.IGdiPen
+    {
+        #region Local Variables
+
+        private int _style;
+        private int _width;
+        private int _color;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object style.
+        /// </summary>
+        public int Style
+        {
+            get
+            {
+                return _style;
+            }
+        }
+
+        /// <summary>
+        /// Object width.
+        /// </summary>
+        public int Width
+        {
+            get
+            {
+                return _width;
+            }
+        }
+
+        /// <summary>
+        /// Object color.
+        /// </summary>
+        public int Color
+        {
+            get
+            {
+                return _color;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Construdor padrão.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="style"></param>
+        /// <param name="width"></param>
+        /// <param name="color"></param>
+        public SvgPen(
+            SvgGdi gdi,
+            int style,
+            int width,
+            int color)
+            :base(gdi)
+        {
+            _style = style;
+            _width = (width > 0) ? width : 1;
+            _color = color;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode() 
+        {
+		    int PRIME = 31;
+		    int result = 1;
+		    result = PRIME * result + _color;
+		    result = PRIME * result + _style;
+		    result = PRIME * result + _width;
+		    return result;
+	    }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj) 
+        {
+		    if (this == obj)
+			    return true;
+		    if (obj == null)
+			    return false;
+		    if (typeof(SvgPen) != obj.GetType())
+			    return false;
+		    SvgPen other = (SvgPen) obj;
+		    if (_color != other._color)
+			    return false;
+		    if (_style != other._style)
+			    return false;
+		    if (_width != other._width)
+			    return false;
+		    return true;
+	    }
+
+        /// <summary>
+        /// Create inner text element.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public System.Xml.XmlText CreateTextNode(String id)
+        {
+            return GDI.Document.CreateTextNode("." + id + " { " + ToString() + " }\r\n");
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+
+            if (_style == (int)Gdi.PenPSEnum.PS_NULL)
+            {
+                buffer.Append("stroke: none; ");
+            }
+            else
+            {
+                // stroke
+                buffer.Append("stroke: " + ToColor(_color) + "; ");
+
+                // stroke-width
+                buffer.Append("stroke-width: " + _width + "; ");
+
+                // stroke-linejoin
+                buffer.Append("stroke-linejoin: round; ");
+
+                // stroke-dasharray
+                if (_width == 1 && (int)Gdi.PenPSEnum.PS_DASH <= _style && _style <= (int)Gdi.PenPSEnum.PS_DASHDOTDOT)
+                {
+                    buffer.Append("stroke-dasharray: ");
+                    switch (_style)
+                    {
+                        case (int)Gdi.PenPSEnum.PS_DASH:
+                            buffer.Append(
+                                "" + ToRealSize(18) + "," + ToRealSize(6));
+                            break;
+                        case (int)Gdi.PenPSEnum.PS_DOT:
+                            buffer.Append("" + ToRealSize(3) + "," + ToRealSize(3));
+                            break;
+                        case (int)Gdi.PenPSEnum.PS_DASHDOT:
+                            buffer.Append(
+                                ""
+                                    + ToRealSize(9)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3));
+                            break;
+                        case (int)Gdi.PenPSEnum.PS_DASHDOTDOT:
+                            buffer.Append(
+                                ""
+                                    + ToRealSize(9)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3)
+                                    + ","
+                                    + ToRealSize(3));
+                            break;
+                    }
+                    buffer.Append("; ");
+                }
+            }
+
+            if (buffer.Length > 0) 
+                buffer.Length = buffer.Length - 1;
+            return buffer.ToString();
+        }
+
+        #endregion
+    }
+}

+ 149 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgRectRegion.cs

@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// Scalable Vector Graphics - Represents SVG Rect Region object.
+    /// </summary>
+    public class SvgRectRegion : SvgRegion
+    {
+        #region Local Variables
+
+        private int _left;
+        private int _top;
+        private int _right;
+        private int _bottom;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Left value.
+        /// </summary>
+        public int Left
+        {
+            get
+            {
+                return _left;
+            }
+        }
+
+        /// <summary>
+        /// Top value.
+        /// </summary>
+        public int Top
+        {
+            get
+            {
+                return _top;
+            }
+        }
+
+        /// <summary>
+        /// Right value.
+        /// </summary>
+        public int Right
+        {
+            get
+            {
+                return _right;
+            }
+        }
+
+        /// <summary>
+        /// Bottom value.
+        /// </summary>
+        public int Bottom
+        {
+            get
+            {
+                return _bottom;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        /// <param name="left"></param>
+        /// <param name="top"></param>
+        /// <param name="right"></param>
+        /// <param name="bottom"></param>
+        public SvgRectRegion(SvgGdi gdi, int left, int top, int right, int bottom)
+            : base(gdi)
+        {
+            _left = left;
+            _top = top;
+            _right = right;
+            _bottom = bottom;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Create rect element.
+        /// </summary>
+        /// <returns></returns>
+        public override System.Xml.XmlElement CreateElement()
+        {
+            System.Xml.XmlElement elem = GDI.Document.CreateElement("rect");
+            elem.SetAttribute("x", "" + (int)GDI.DC.ToAbsoluteX(Left));
+            elem.SetAttribute("y", "" + (int)GDI.DC.ToAbsoluteY(Top));
+            elem.SetAttribute("width", "" + (int)GDI.DC.ToRelativeX(Right - Left));
+            elem.SetAttribute("height", "" + (int)GDI.DC.ToRelativeY(Bottom - Top));
+            return elem;
+        }
+
+        /// <summary>
+        /// Serves as the default hash function.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode() {
+		    int prime = 31;
+		    int result = 1;
+		    result = prime * result + _bottom;
+		    result = prime * result + _left;
+		    result = prime * result + _right;
+		    result = prime * result + _top;
+		    return result;
+	    }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(Object obj)
+        {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (typeof(SvgRectRegion) != obj.GetType())
+                return false;
+            SvgRectRegion other = (SvgRectRegion)obj;
+            if (_bottom != other._bottom)
+                return false;
+            if (_left != other._left)
+                return false;
+            if (_right != other._right)
+                return false;
+            if (_top != other._top)
+                return false;
+            return true;
+        }
+
+        #endregion
+    }
+}

+ 38 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Svg/SvgRegion.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Svg
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public abstract class SvgRegion : SvgObject, Gdi.IGdiRegion
+    {
+        #region Constructors
+
+        /// <summary>
+        /// Default Constructor.
+        /// </summary>
+        /// <param name="gdi"></param>
+        public SvgRegion(SvgGdi gdi)
+            : base(gdi)
+        {
+
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Create element - abstract method.
+        /// </summary>
+        /// <returns></returns>
+        public abstract System.Xml.XmlElement CreateElement();
+
+        #endregion
+    }
+}

+ 84 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Util/Base64.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Util
+{
+    /// <summary>
+    /// Base 64 encode
+    /// </summary>
+    public class Base64
+    {
+        #region Local Variables
+
+        private static char[] ENCODE_DATA = {
+		    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+		    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+		    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+		    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+		    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+		    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+		    'w', 'x', 'y', 'z', '0', '1', '2', '3',
+		    '4', '5', '6', '7', '8', '9', '+', '/' 
+	    };
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Encode byte array.
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+	    public static string Encode(byte[] data) 
+        {
+		    char[] buffer = null;
+
+		    if (data.Length % 3 == 0) 
+			    buffer = new char[data.Length / 3 * 4];
+		    else 
+			    buffer = new char[(data.Length / 3 + 1) * 4];
+		    
+
+		    int buf = 0;
+		    for (int i = 0; i < data.Length; i++) 
+            {
+			    switch (i % 3) 
+                {
+				    case 0 :
+					    buffer[i / 3 * 4] = ENCODE_DATA[(data[i] & 0xFC) >> 2];
+					    buf = (data[i] & 0x03) << 4;
+					    if (i + 1 == data.Length) 
+                        {
+						    buffer[i / 3 * 4 + 1] = ENCODE_DATA[buf];
+						    buffer[i / 3 * 4 + 2] = '=';
+						    buffer[i / 3 * 4 + 3] = '=';
+					    }
+					    break;
+				    case 1 :
+					    buf += (data[i] & 0xF0) >> 4;
+					    buffer[i / 3 * 4 + 1] = ENCODE_DATA[buf];
+					    buf = (data[i] & 0x0F) << 2;
+					    if (i + 1 == data.Length) 
+                        {
+						    buffer[i / 3 * 4 + 2] = ENCODE_DATA[buf];
+						    buffer[i / 3 * 4 + 3] = '=';
+					    }
+					    break;
+				    case 2 :
+					    buf += (data[i] & 0xC0) >> 6;
+					    buffer[i / 3 * 4 + 2] = ENCODE_DATA[buf];
+					    buffer[i / 3 * 4 + 3] = ENCODE_DATA[data[i] & 0x3F];
+					    break;
+			    }
+		    }
+
+		    return new string(buffer);
+        }
+
+        #endregion
+    }
+}

+ 15 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Util/ImageUtil.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Util
+{
+    
+    public class ImageUtil
+    {
+       //Not implemented.
+	
+    }
+}

+ 74 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfBrush.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Brush object.
+    /// </summary>
+    public class WmfBrush : WmfObject, Gdi.IGdiBrush
+    {
+        #region Local Variables
+
+        private int _style;
+        private int _color;
+        private int _hatch;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object Style.
+        /// </summary>
+        public int Style
+        {
+            get
+            {
+                return _style;
+            }
+        }
+
+        /// <summary>
+        /// Object Color.
+        /// </summary>
+        public int Color
+        {
+            get
+            {
+                return _color;
+            }
+        }
+
+        /// <summary>
+        /// Object Hatch.
+        /// </summary>
+        public int Hatch
+        {
+            get
+            {
+                return _hatch;
+            }
+        }
+
+        /// <summary>
+        /// Default Constructor
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="style"></param>
+        /// <param name="color"></param>
+        /// <param name="hatch"></param>
+        public WmfBrush(int id, int style, int color, int hatch)
+            :base (id)
+        {
+            _style = style;
+            _color = color;
+            _hatch = hatch;
+        }
+
+        #endregion
+    }
+}

+ 85 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfConstants.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Constants Enum.
+    /// </summary>
+    public enum WmfConstants
+    {
+         RECORD_EOF = 0x0000,
+         RECORD_REALIZE_PALETTE = 0x0035,
+         RECORD_SET_PALETTE_ENTRIES = 0x0037,
+         RECORD_SET_BK_MODE = 0x0102,
+         RECORD_SET_MAP_MODE = 0x0103,
+         RECORD_SET_ROP2 = 0x0104,
+         RECORD_SET_REL_ABS = 0x0105,
+         RECORD_SET_POLY_FILL_MODE = 0x0106,
+         RECORD_SET_STRETCH_BLT_MODE = 0x0107,
+         RECORD_SET_TEXT_CHARACTER_EXTRA = 0x0108,
+         RECORD_RESTORE_DC = 0x0127,
+         RECORD_RESIZE_PALETTE = 0x0139,
+         RECORD_DIB_CREATE_PATTERN_BRUSH = 0x0142,
+         RECORD_SET_LAYOUT = 0x0149,
+         RECORD_SET_BK_COLOR = 0x0201,
+         RECORD_SET_TEXT_COLOR = 0x0209,
+         RECORD_OFFSET_VIEWPORT_ORG_EX = 0x0211,
+         RECORD_LINE_TO = 0x0213,
+         RECORD_MOVE_TO_EX = 0x0214,
+         RECORD_OFFSET_CLIP_RGN = 0x0220,
+         RECORD_FILL_RGN = 0x0228,
+         RECORD_SET_MAPPER_FLAGS = 0x0231,
+         RECORD_SELECT_PALETTE = 0x0234,
+         RECORD_POLYGON = 0x0324,
+         RECORD_POLYLINE = 0x0325,
+         RECORD_SET_TEXT_JUSTIFICATION = 0x020A,
+         RECORD_SET_WINDOW_ORG_EX = 0x020B,
+         RECORD_SET_WINDOW_EXT_EX = 0x020C,
+         RECORD_SET_VIEWPORT_ORG_EX = 0x020D,
+         RECORD_SET_VIEWPORT_EXT_EX = 0x020E,
+         RECORD_OFFSET_WINDOW_ORG_EX = 0x020F,
+         RECORD_SCALE_WINDOW_EXT_EX = 0x0410,
+         RECORD_SCALE_VIEWPORT_EXT_EX = 0x0412,
+         RECORD_EXCLUDE_CLIP_RECT = 0x0415,
+         RECORD_INTERSECT_CLIP_RECT = 0x0416,
+         RECORD_ELLIPSE = 0x0418,
+         RECORD_FLOOD_FILL = 0x0419,
+         RECORD_FRAME_RGN = 0x0429,
+         RECORD_ANIMATE_PALETTE = 0x0436,
+         RECORD_TEXT_OUT = 0x0521,
+         RECORD_POLY_POLYGON = 0x0538,
+         RECORD_EXT_FLOOD_FILL = 0x0548,
+         RECORD_RECTANGLE = 0x041B,
+         RECORD_SET_PIXEL = 0x041F,
+         RECORD_ROUND_RECT = 0x061C,
+         RECORD_PAT_BLT = 0x061D,
+         RECORD_SAVE_DC = 0x001E,
+         RECORD_PIE = 0x081A,
+         RECORD_STRETCH_BLT = 0x0B23,
+         RECORD_ESCAPE = 0x0626,
+         RECORD_INVERT_RGN = 0x012A,
+         RECORD_PAINT_RGN = 0x012B,
+         RECORD_SELECT_CLIP_RGN = 0x012C,
+         RECORD_SELECT_OBJECT = 0x012D,
+         RECORD_SET_TEXT_ALIGN = 0x012E,
+         RECORD_ARC = 0x0817,
+         RECORD_CHORD = 0x0830,
+         RECORD_BIT_BLT = 0x0922,
+         RECORD_EXT_TEXT_OUT = 0x0a32,
+         RECORD_SET_DIBITS_TO_DEVICE = 0x0d33,
+         RECORD_DIB_BIT_BLT = 0x0940,
+         RECORD_DIB_STRETCH_BLT = 0x0b41,
+         RECORD_STRETCH_DIBITS = 0x0f43,
+         RECORD_DELETE_OBJECT = 0x01f0,
+         RECORD_CREATE_PALETTE = 0x00f7,
+         RECORD_CREATE_PATTERN_BRUSH = 0x01F9,
+         RECORD_CREATE_PEN_INDIRECT = 0x02FA,
+         RECORD_CREATE_FONT_INDIRECT = 0x02FB,
+         RECORD_CREATE_BRUSH_INDIRECT = 0x02FC,
+         RECORD_CREATE_RECT_RGN = 0x06FF,
+    }
+}

+ 250 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfFont.cs

@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Font object.
+    /// </summary>
+    public class WmfFont : WmfObject, Gdi.IGdiFont
+    {
+        #region Local Variables
+
+        private int _height;
+        private int _width;
+        private int _escapement;
+        private int _orientation;
+        private int _weight;
+        private bool _italic;
+        private bool _underline;
+        private bool _strikeout;
+        private int _charset;
+        private int _outPrecision;
+        private int _clipPrecision;
+        private int _quality;
+        private int _pitchAndFamily;
+
+        private string _faceName;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object Height.
+        /// </summary>
+        public int Height
+        {
+            get
+            {
+                return _height;
+            }
+        }
+
+        /// <summary>
+        /// Object Width.
+        /// </summary>
+        public int Width
+        {
+            get
+            {
+                return _width;
+            }
+        }
+
+        /// <summary>
+        /// Object Escapement.
+        /// </summary>
+        public int Escapement
+        {
+            get
+            {
+                return _escapement;
+            }
+        }
+
+        /// <summary>
+        /// Object Orientation.
+        /// </summary>
+        public int Orientation
+        {
+            get
+            {
+                return _orientation;
+            }
+        }
+
+        /// <summary>
+        /// Object Weight.
+        /// </summary>
+        public int Weight
+        {
+            get
+            {
+                return _weight;
+            }
+        }
+
+        /// <summary>
+        /// Defines whether the font is italic.
+        /// </summary>
+        public bool IsItalic
+        {
+            get
+            {
+                return _italic;
+            }
+        }
+
+        /// <summary>
+        /// Defines whether the font is underlined.
+        /// </summary>
+        public bool IsUnderlined
+        {
+            get
+            {
+                return _underline;
+            }
+        }
+
+        /// <summary>
+        /// Defines whether the font is striked.
+        /// </summary>
+        public bool IsStrikedOut
+        {
+            get
+            {
+                return _strikeout;
+            }
+        }
+
+        /// <summary>
+        /// Defines the font charset.
+        /// </summary>
+        public int Charset
+        {
+            get
+            {
+                return _charset;
+            }
+        }
+
+        /// <summary>
+        /// Defines the font out precision.
+        /// </summary>
+        public int OutPrecision
+        {
+            get
+            {
+                return _outPrecision;
+            }
+        }
+
+        /// <summary>
+        /// Defines the clip precision.
+        /// </summary>
+        public int ClipPrecision
+        {
+            get
+            {
+                return _clipPrecision;
+            }
+        }
+
+        /// <summary>
+        /// Object quality.
+        /// </summary>
+        public int Quality
+        {
+            get
+            {
+                return _quality;
+            }
+        }
+
+        /// <summary>
+        /// Defines pitch and famility font.
+        /// </summary>
+        public int PitchAndFamily
+        {
+            get
+            {
+                return _pitchAndFamily;
+            }
+        }
+
+        /// <summary>
+        /// Defines face name rules.
+        /// </summary>
+        public string FaceName
+        {
+            get
+            {
+                return _faceName;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default Constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="height"></param>
+        /// <param name="width"></param>
+        /// <param name="escapement"></param>
+        /// <param name="orientation"></param>
+        /// <param name="weight"></param>
+        /// <param name="italic"></param>
+        /// <param name="underline"></param>
+        /// <param name="strikeout"></param>
+        /// <param name="charset"></param>
+        /// <param name="outPrecision"></param>
+        /// <param name="clipPrecision"></param>
+        /// <param name="quality"></param>
+        /// <param name="pitchAndFamily"></param>
+        /// <param name="faceName"></param>
+        public WmfFont(int id,
+            int height,
+            int width,
+            int escapement,
+            int orientation,
+            int weight,
+            bool italic,
+            bool underline,
+            bool strikeout,
+            int charset,
+            int outPrecision,
+            int clipPrecision,
+            int quality,
+            int pitchAndFamily,
+            byte[] faceName)
+            : base (id)
+        {
+
+            _height = height;
+            _width = width;
+            _escapement = escapement;
+            _orientation = orientation;
+            _weight = weight;
+            _italic = italic;
+            _underline = underline;
+            _strikeout = strikeout;
+            _charset = charset;
+            _outPrecision = outPrecision;
+            _clipPrecision = clipPrecision;
+            _quality = quality;
+            _pitchAndFamily = pitchAndFamily;
+            _faceName = WMFConverter.Gdi.GdiUtils.ConvertString(faceName, charset);
+        }
+
+        #endregion
+
+       
+    }
+}

File diff suppressed because it is too large
+ 1647 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfGdi.cs


+ 49 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfObject.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Object base
+    /// </summary>
+    public class WmfObject : Gdi.IGdiObject
+    {
+        #region Local Variables
+
+        public int _id;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object Id.
+        /// </summary>
+        public int Id
+        {
+            get
+            {
+                return _id;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        public WmfObject(int id)
+        {
+            _id = id;
+        }
+
+        #endregion
+       
+    }
+}

+ 65 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPalette.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Palette object.
+    /// </summary>
+    public class WmfPalette: WmfObject, Gdi.IGdiPalette
+    {
+        #region Local Variables
+
+        private int _version;
+        private int[] _entries;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object version.
+        /// </summary>
+        public int Version
+        {
+            get
+            {
+                return _version;
+            }
+        }
+
+        /// <summary>
+        /// Object entries.
+        /// </summary>
+        public int[] Entries
+        {
+            get
+            {
+                return _entries;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="version"></param>
+        /// <param name="entries"></param>
+        public WmfPalette(int id, int version, int[] entries)
+            : base(id)
+        {
+            _version = version;
+            _entries = entries;
+        }
+
+        #endregion
+
+    }
+}

+ 30 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfParseException.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    public class WmfParseException : Exception
+    {
+        public WmfParseException()
+            :base()
+        {
+
+        }
+
+        public WmfParseException(String message)
+            : base(message)
+        {
+
+        }
+
+        public WmfParseException(String message, Exception t)
+            : base(message,t)
+        {
+
+        }
+
+    }
+}

+ 821 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfParser.cs

@@ -0,0 +1,821 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Parse WMF to SVG 
+    /// </summary>
+    public class WmfParser
+    {
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        public WmfParser()
+        {
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        /// <summary>
+        /// Parse WMF file to SVG file.
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <param name="gdi"></param>
+        public void Parse(System.IO.Stream stream, Gdi.IGdi gdi) 
+        {
+            WMFConverter.IO.DataInput binReader = null;
+            bool isEmpty = true;
+
+            try
+            {
+                binReader = new WMFConverter.IO.DataInput(stream, true);
+
+                int mtType = 0;
+                int mtHeaderSize = 0;
+
+                long key = binReader.ReadUint32();
+                isEmpty = false;
+                if (key == -1698247209)//0x9AC6CDD7)
+                {
+                    int hmf = binReader.ReadInt16();
+                    int vsx = binReader.ReadInt16();
+                    int vsy = binReader.ReadInt16();
+                    int vex = binReader.ReadInt16();
+                    int vey = binReader.ReadInt16();
+                    int dpi = binReader.ReadUint16();
+                    long reserved = binReader.ReadUint32();
+                    int checksum = binReader.ReadUint16();
+
+                    gdi.PlaceableHeader(vsx, vsy, vex, vey, dpi);
+
+                    mtType = binReader.ReadUint16();
+                    mtHeaderSize = binReader.ReadUint16();
+                }
+                else
+                {
+                    mtType = (int)(key & 0x0000FFFF);
+                    mtHeaderSize = (int)((key & 0xFFFF0000) >> 16);
+                }
+
+                int mtVersion = binReader.ReadUint16();
+                long mtSize = binReader.ReadUint32();
+                int mtNoObjects = binReader.ReadUint16();
+                long mtMaxRecord = binReader.ReadUint32();
+                int mtNoParameters = binReader.ReadUint16();
+
+                if (mtType != 1 || mtHeaderSize != 9)
+                {
+                    throw new WmfParseException("invalid file format.");
+                }
+
+                gdi.Header();
+
+                WMFConverter.Gdi.IGdiObject[] objs = new WMFConverter.Gdi.IGdiObject[mtNoObjects];
+
+                while (true)
+                {
+                    int size = (int)binReader.ReadUint32() - 3;
+                    int id = binReader.ReadUint16();
+
+                    if (id == (int)WmfConstants.RECORD_EOF)
+                        break; // Last record
+
+                    binReader.Count = 0;
+
+                    switch (id)
+                    {
+                        case (int)WmfConstants.RECORD_REALIZE_PALETTE:
+                            gdi.RealizePalette();
+                            break;
+                        case (int)WmfConstants.RECORD_SET_PALETTE_ENTRIES:
+                            {
+                                int[] entries = new int[binReader.ReadUint16()];
+                                int startIndex = binReader.ReadUint16();
+                                int objID = binReader.ReadUint16();
+                                for (int i = 0; i < entries.Length; i++)
+                                    entries[i] = binReader.ReadInt32();
+                                gdi.SetPaletteEntries((Gdi.IGdiPalette)objs[objID], startIndex, entries);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_BK_MODE:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetBkMode(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_MAP_MODE:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetMapMode(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_ROP2:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetROP2(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_REL_ABS:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetRelAbs(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_POLY_FILL_MODE:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetPolyFillMode(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_STRETCH_BLT_MODE:
+                            {
+                                int mode = binReader.ReadInt16();
+                                gdi.SetStretchBltMode(mode);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_TEXT_CHARACTER_EXTRA:
+                            {
+                                int extra = binReader.ReadInt16();
+                                gdi.SetTextCharacterExtra(extra);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_RESTORE_DC:
+                            {
+                                int dc = binReader.ReadInt16();
+                                gdi.RestoreDC(dc);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_RESIZE_PALETTE:
+                            {
+                                int objID = binReader.ReadUint16();
+                                gdi.ResizePalette((Gdi.IGdiPalette)objs[objID]);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_DIB_CREATE_PATTERN_BRUSH:
+                            {
+                                int usage = binReader.ReadInt32();
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.DibCreatePatternBrush(image, usage);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_LAYOUT:
+                            {
+                                long layout = binReader.ReadUint32();
+                                gdi.SetLayout(layout);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_BK_COLOR:
+                            {
+                                int color = binReader.ReadInt32();
+                                gdi.SetBkColor(color);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_TEXT_COLOR:
+                            {
+                                int color = binReader.ReadInt32();
+                                gdi.SetTextColor(color);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_OFFSET_VIEWPORT_ORG_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.OffsetViewportOrgEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_LINE_TO:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                gdi.LineTo(ex, ey);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_MOVE_TO_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.MoveToEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_OFFSET_CLIP_RGN:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.OffsetClipRgn(x, y);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_FILL_RGN:
+                            {
+                                int brushID = binReader.ReadUint16();
+                                int rgnID = binReader.ReadUint16();
+                                gdi.FillRgn((Gdi.IGdiRegion)objs[rgnID], (Gdi.IGdiBrush)objs[brushID]);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_MAPPER_FLAGS:
+                            {
+                                long flag = binReader.ReadUint32();
+                                gdi.SetMapperFlags(flag);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SELECT_PALETTE:
+                            {
+                                bool mode = (binReader.ReadInt16() != 0);
+                                if ((size * 2 - binReader.Count) > 0)
+                                {
+                                    int objID = binReader.ReadUint16();
+                                    gdi.SelectPalette((Gdi.IGdiPalette)objs[objID], mode);
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_POLYGON:
+                            {
+                                WMFConverter.Gdi.Point[] points = new WMFConverter.Gdi.Point[binReader.ReadInt16()];
+                                for (int i = 0; i < points.Length; i++)
+                                    points[i] = new WMFConverter.Gdi.Point(binReader.ReadInt16(), binReader.ReadInt16());
+
+                                gdi.Polygon(points);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_POLYLINE:
+                            {
+                                WMFConverter.Gdi.Point[] points = new WMFConverter.Gdi.Point[binReader.ReadInt16()];
+                                for (int i = 0; i < points.Length; i++)
+                                    points[i] = new WMFConverter.Gdi.Point(binReader.ReadInt16(), binReader.ReadInt16());
+
+                                gdi.Polyline(points);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_TEXT_JUSTIFICATION:
+                            {
+                                int breakCount = binReader.ReadInt16();
+                                int breakExtra = binReader.ReadInt16();
+                                gdi.SetTextJustification(breakExtra, breakCount);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_WINDOW_ORG_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.SetWindowOrgEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_WINDOW_EXT_EX:
+                            {
+                                int height = binReader.ReadInt16();
+                                int width = binReader.ReadInt16();
+                                gdi.SetWindowExtEx(width, height, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_VIEWPORT_ORG_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.SetViewportOrgEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_VIEWPORT_EXT_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.SetViewportExtEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_OFFSET_WINDOW_ORG_EX:
+                            {
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.OffsetWindowOrgEx(x, y, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SCALE_WINDOW_EXT_EX:
+                            {
+                                int yd = binReader.ReadInt16();
+                                int y = binReader.ReadInt16();
+                                int xd = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.ScaleWindowExtEx(x, xd, y, yd, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SCALE_VIEWPORT_EXT_EX:
+                            {
+                                int yd = binReader.ReadInt16();
+                                int y = binReader.ReadInt16();
+                                int xd = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.ScaleViewportExtEx(x, xd, y, yd, null);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_EXCLUDE_CLIP_RECT:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.ExcludeClipRect(sx, sy, ex, ey);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_INTERSECT_CLIP_RECT:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.IntersectClipRect(sx, sy, ex, ey);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_ELLIPSE:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.Ellipse(sx, sy, ex, ey);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_FLOOD_FILL:
+                            {
+                                int color = binReader.ReadInt32();
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.FloodFill(x, y, color);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_FRAME_RGN:
+                            {
+                                int height = binReader.ReadInt16();
+                                int width = binReader.ReadInt16();
+                                int brushID = binReader.ReadUint16();
+                                int rgnID = binReader.ReadUint16();
+                                gdi.FrameRgn((Gdi.IGdiRegion)objs[rgnID], (Gdi.IGdiBrush)objs[brushID], width, height);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_ANIMATE_PALETTE:
+                            {
+                                int[] entries = new int[binReader.ReadUint16()];
+                                int startIndex = binReader.ReadUint16();
+                                int objID = binReader.ReadUint16();
+                                for (int i = 0; i < entries.Length; i++)
+                                    entries[i] = binReader.ReadInt32();
+
+                                gdi.AnimatePalette((Gdi.IGdiPalette)objs[objID], startIndex, entries);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_TEXT_OUT:
+                            {
+                                int count = binReader.ReadInt16();
+                                byte[] text = binReader.ReadBytes(count);
+                                if (count % 2 == 1)
+                                {
+                                    binReader.ReadByte();
+                                }
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.TextOut(x, y, text);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_POLY_POLYGON:
+                            {
+                                WMFConverter.Gdi.Point[][] points = new WMFConverter.Gdi.Point[binReader.ReadInt16()][];
+                                for (int i = 0; i < points.Length; i++)
+                                    points[i] = new WMFConverter.Gdi.Point[binReader.ReadInt16()];
+
+                                for (int i = 0; i < points.Length; i++)
+                                    for (int j = 0; j < points[i].Length; j++)
+                                        points[i][j] = new WMFConverter.Gdi.Point(binReader.ReadInt16(), binReader.ReadInt16());
+
+
+                                gdi.PolyPolygon(points);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_EXT_FLOOD_FILL:
+                            {
+                                int type = binReader.ReadUint16();
+                                int color = binReader.ReadInt32();
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.ExtFloodFill(x, y, color, type);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_RECTANGLE:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.Rectangle(sx, sy, ex, ey);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_PIXEL:
+                            {
+                                int color = binReader.ReadInt32();
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.SetPixel(x, y, color);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_ROUND_RECT:
+                            {
+                                int rh = binReader.ReadInt16();
+                                int rw = binReader.ReadInt16();
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.RoundRect(sx, sy, ex, ey, rw, rh);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_PAT_BLT:
+                            {
+                                long rop = binReader.ReadUint32();
+                                int height = binReader.ReadInt16();
+                                int width = binReader.ReadInt16();
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                gdi.PatBlt(x, y, width, height, rop);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SAVE_DC:
+                            {
+                                gdi.SeveDC();
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_PIE:
+                            {
+                                int eyr = binReader.ReadInt16();
+                                int exr = binReader.ReadInt16();
+                                int syr = binReader.ReadInt16();
+                                int sxr = binReader.ReadInt16();
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                gdi.Pie(sx, sy, ex, ey, sxr, syr, exr, eyr);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_STRETCH_BLT:
+                            {
+                                long rop = binReader.ReadUint32();
+                                int sh = binReader.ReadInt16();
+                                int sw = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int dh = binReader.ReadInt16();
+                                int dw = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                gdi.StretchBlt(image, dx, dy, dw, dh, sx, sy, sw, sh, rop);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_ESCAPE:
+                            {
+                                byte[] data = binReader.ReadBytes(2 * size);
+                                gdi.Escape(data);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_INVERT_RGN:
+                            {
+                                int rgnID = binReader.ReadUint16();
+                                gdi.InvertRgn((Gdi.IGdiRegion)objs[rgnID]);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_PAINT_RGN:
+                            {
+                                int objID = binReader.ReadUint16();
+                                gdi.PaintRgn((Gdi.IGdiRegion)objs[objID]);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SELECT_CLIP_RGN:
+                            {
+                                int objID = binReader.ReadUint16();
+                                Gdi.IGdiRegion rgn = (objID > 0) ? (Gdi.IGdiRegion)objs[objID] : null;
+                                gdi.SelectClipRgn(rgn);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SELECT_OBJECT:
+                            {
+                                int objID = binReader.ReadUint16();
+                                gdi.SelectObject(objs[objID]);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_TEXT_ALIGN:
+                            {
+                                int align = binReader.ReadInt16();
+                                gdi.SetTextAlign(align);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_ARC:
+                            {
+                                int eya = binReader.ReadInt16();
+                                int exa = binReader.ReadInt16();
+                                int sya = binReader.ReadInt16();
+                                int sxa = binReader.ReadInt16();
+                                int eyr = binReader.ReadInt16();
+                                int exr = binReader.ReadInt16();
+                                int syr = binReader.ReadInt16();
+                                int sxr = binReader.ReadInt16();
+                                gdi.Arc(sxr, syr, exr, eyr, sxa, sya, exa, eya);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CHORD:
+                            {
+                                int eya = binReader.ReadInt16();
+                                int exa = binReader.ReadInt16();
+                                int sya = binReader.ReadInt16();
+                                int sxa = binReader.ReadInt16();
+                                int eyr = binReader.ReadInt16();
+                                int exr = binReader.ReadInt16();
+                                int syr = binReader.ReadInt16();
+                                int sxr = binReader.ReadInt16();
+                                gdi.Chord(sxr, syr, exr, eyr, sxa, sya, exa, eya);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_BIT_BLT:
+                            {
+                                long rop = binReader.ReadUint32();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int height = binReader.ReadInt16();
+                                int width = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                gdi.BitBlt(image, dx, dy, width, height, sx, sy, rop);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_EXT_TEXT_OUT:
+                            {
+                                int rsize = size;
+
+                                int y = binReader.ReadInt16();
+                                int x = binReader.ReadInt16();
+                                int count = binReader.ReadInt16();
+                                int options = binReader.ReadUint16();
+                                rsize -= 4;
+
+                                int[] rect = null;
+                                if ((options & 0x0006) > 0)
+                                {
+                                    rect = new int[] { binReader.ReadInt16(), binReader.ReadInt16(), binReader.ReadInt16(), binReader.ReadInt16() };
+                                    rsize -= 4;
+                                }
+                                byte[] text = binReader.ReadBytes(count);
+                                if (count % 2 == 1)
+                                    binReader.ReadByte();
+
+                                rsize -= (count + 1) / 2;
+
+                                int[] dx = null;
+                                if (rsize > 0)
+                                {
+                                    dx = new int[rsize];
+                                    for (int i = 0; i < dx.Length; i++)
+                                        dx[i] = binReader.ReadInt16();
+                                }
+                                gdi.ExtTextOut(x, y, options, rect, text, dx);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_SET_DIBITS_TO_DEVICE:
+                            {
+                                int colorUse = binReader.ReadUint16();
+                                int scanlines = binReader.ReadUint16();
+                                int startscan = binReader.ReadUint16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int dh = binReader.ReadInt16();
+                                int dw = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                gdi.SetDIBitsToDevice(dx, dy, dw, dh, sx, sy, startscan, scanlines, image, colorUse);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_DIB_BIT_BLT:
+                            {
+                                bool isRop = false;
+
+                                long rop = binReader.ReadUint32();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int height = binReader.ReadInt16();
+                                if (height == 0)
+                                {
+                                    height = binReader.ReadInt16();
+                                    isRop = true;
+                                }
+                                int width = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                if (isRop)
+                                    gdi.DibBitBlt(null, dx, dy, width, height, sx, sy, rop);
+                                else
+                                {
+                                    byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                    gdi.DibBitBlt(image, dx, dy, width, height, sx, sy, rop);
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_DIB_STRETCH_BLT:
+                            {
+                                long rop = binReader.ReadUint32();
+                                int sh = binReader.ReadInt16();
+                                int sw = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int dh = binReader.ReadInt16();
+                                int dw = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                gdi.DibStretchBlt(image, dx, dy, dw, dh, sx, sy, sw, sh, rop);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_STRETCH_DIBITS:
+                            {
+                                long rop = binReader.ReadUint32();
+                                int usage = binReader.ReadUint16();
+                                int sh = binReader.ReadInt16();
+                                int sw = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                int dh = binReader.ReadInt16();
+                                int dw = binReader.ReadInt16();
+                                int dy = binReader.ReadInt16();
+                                int dx = binReader.ReadInt16();
+
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                gdi.StretchDIBits(dx, dy, dw, dh, sx, sy, sw, sh, image, usage, rop);
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_DELETE_OBJECT:
+                            {
+                                int objID = binReader.ReadUint16();
+                                gdi.DeleteObject(objs[objID]);
+                                objs[objID] = null;
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_PALETTE:
+                            {
+                                int version = binReader.ReadUint16();
+                                int[] entries = new int[binReader.ReadUint16()];
+                                for (int i = 0; i < entries.Length; i++)
+                                    entries[i] = binReader.ReadInt32();
+
+
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.CreatePalette(version, entries);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_PATTERN_BRUSH:
+                            {
+                                byte[] image = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.CreatePatternBrush(image);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_PEN_INDIRECT:
+                            {
+                                int style = binReader.ReadUint16();
+                                int width = binReader.ReadInt16();
+                                binReader.ReadInt16();
+                                int color = binReader.ReadInt32();
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.CreatePenIndirect(style, width, color);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_FONT_INDIRECT:
+                            {
+                                int height = binReader.ReadInt16();
+                                int width = binReader.ReadInt16();
+                                int escapement = binReader.ReadInt16();
+                                int orientation = binReader.ReadInt16();
+                                int weight = binReader.ReadInt16();
+                                bool italic = (binReader.ReadByte() == 1);
+                                bool underline = (binReader.ReadByte() == 1);
+                                bool strikeout = (binReader.ReadByte() == 1);
+                                int charset = binReader.ReadByte();
+                                int outPrecision = binReader.ReadByte();
+                                int clipPrecision = binReader.ReadByte();
+                                int quality = binReader.ReadByte();
+                                int pitchAndFamily = binReader.ReadByte();
+                                byte[] faceName = binReader.ReadBytes(size * 2 - binReader.Count);
+
+                                Gdi.IGdiObject obj = gdi.CreateFontIndirect(height, width, escapement, orientation, weight, italic,
+                                        underline, strikeout, charset, outPrecision, clipPrecision, quality, pitchAndFamily,
+                                        faceName);
+
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = obj;
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_BRUSH_INDIRECT:
+                            {
+                                int style = binReader.ReadUint16();
+                                int color = binReader.ReadInt32();
+                                int hatch = binReader.ReadUint16();
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.CreateBrushIndirect(style, color, hatch);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        case (int)WmfConstants.RECORD_CREATE_RECT_RGN:
+                            {
+                                int ey = binReader.ReadInt16();
+                                int ex = binReader.ReadInt16();
+                                int sy = binReader.ReadInt16();
+                                int sx = binReader.ReadInt16();
+                                for (int i = 0; i < objs.Length; i++)
+                                {
+                                    if (objs[i] == null)
+                                    {
+                                        objs[i] = gdi.CreateRectRgn(sx, sy, ex, ey);
+                                        break;
+                                    }
+                                }
+                                break;
+                            }
+                        default:
+                            {
+                                //log.fine("unsuppored id find: " + id + " (size=" + size + ")");
+                                Console.Write("unsuppored id find:" + id + "(size=" + size + ")");
+                                break;
+                            }
+                    }
+
+                    int rest = size * 2 - binReader.Count;
+                    for (int i = 0; i < rest; i++)
+                    {
+                        binReader.ReadByte();
+                    }
+                }
+                binReader.Close();
+
+                gdi.Footer();
+            }
+            catch (Exception)
+            {
+                if (isEmpty) 
+                    throw new WmfParseException("input file size is zero.");
+            }
+        }
+
+        #endregion
+    }
+}

+ 52 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPatternBrush.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Pattern Brush object.
+    /// </summary>
+    public class WmfPatternBrush : WmfObject, Gdi.IGdiPatternBrush
+    {
+        #region Local Variables
+
+        private byte[] _image;
+
+        #endregion 
+
+        #region Properties
+
+        /// <summary>
+        /// Object pattern.
+        /// </summary>
+        public byte[] Pattern
+        {
+            get
+            {
+                return _image;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="image"></param>
+        public WmfPatternBrush(int id, byte[] image)
+            : base (id)
+        {
+            _image = image;
+        }
+
+        #endregion 
+
+       
+    }
+}

+ 75 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfPen.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Pen object.
+    /// </summary>
+    class WmfPen: WmfObject,Gdi.IGdiPen
+    {
+        #region Local Variables
+
+        private int _style;
+        private int _width;
+        private int _color;
+
+        #endregion
+
+        #region Properties
+
+        /// <summary>
+        /// Object style.
+        /// </summary>
+        public int Style
+        {
+            get
+            {
+                return _style;
+            }
+        }
+
+        /// <summary>
+        /// Object width.
+        /// </summary>
+        public int Width
+        {
+            get
+            {
+                return _width;
+            }
+        }
+
+        /// <summary>
+        /// Object color.
+        /// </summary>
+        public int Color
+        {
+            get
+            {
+                return _color;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="style"></param>
+        /// <param name="width"></param>
+        /// <param name="color"></param>
+        public WmfPen(int id, int style, int width, int color)
+            : base(id)
+        {
+        }
+
+        #endregion
+    }
+}

+ 92 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfRectRegion.cs

@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Representes WMF RecRegion object.
+    /// </summary>
+    public class WmfRectRegion: WmfObject, Gdi.IGdiRegion
+    {
+        #region Local Variables
+
+        private int _left;
+        private int _top;
+        private int _right;
+        private int _bottom;
+
+        #endregion
+
+        #region Properties
+        
+        /// <summary>
+        /// Get left value.
+        /// </summary>
+        public int Left
+        {
+            get
+            {
+                return _left;
+            }
+        }
+
+        /// <summary>
+        /// Get top value.
+        /// </summary>
+        public int Top
+        {
+            get
+            {
+                return _top;
+            }
+        }
+
+        /// <summary>
+        /// Get right value.
+        /// </summary>
+        public int Right
+        {
+            get
+            {
+                return _right;
+            }
+        }
+
+        /// <summary>
+        /// Get Bottom value.
+        /// </summary>
+        public int Bottom
+        {
+            get
+            {
+                return _bottom;
+            }
+        }
+
+        #endregion
+
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="left"></param>
+        /// <param name="top"></param>
+        /// <param name="right"></param>
+        /// <param name="bottom"></param>
+        public WmfRectRegion(int id, int left, int top, int right, int bottom)
+            :base(id)
+        {
+            _left = left;
+            _top = top;
+            _right = right;
+            _bottom = bottom;
+        }
+
+        #endregion
+    }
+}

+ 27 - 0
TEAMModelOS.SDK/Helper/Common/WMFConverter/Wmf/WmfRegion.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WMFConverter.Wmf
+{
+    /// <summary>
+    /// Windows Metafile - Represents WMF Region.
+    /// </summary>
+    public class WmfRegion : WmfObject,Gdi.IGdiRegion
+    {
+        #region Constructors
+
+        /// <summary>
+        /// Default constructor.
+        /// </summary>
+        /// <param name="id"></param>
+        public WmfRegion(int id)
+            :base (id)
+        {
+        }
+
+        #endregion
+    }
+}

+ 19 - 0
TEAMModelOS.SDK/Module/AzureBlob/Container/AzureBlobModel.cs

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
 using Microsoft.WindowsAzure.Storage.Table;
 using System;
 using System.ComponentModel.DataAnnotations;
+using System.IO;
 using TEAMModelOS.SDK.Helper.Common.DateTimeHelper;
 
 namespace TEAMModelOS.SDK.Module.AzureBlob.Container
@@ -35,6 +36,24 @@ namespace TEAMModelOS.SDK.Module.AzureBlob.Container
             Extension = f.FileName.Substring(f.FileName.LastIndexOf(".") + 1, (f.FileName.Length - f.FileName.LastIndexOf(".") - 1)); //扩展名
             Timestamp = DateTimeHelper.ConvertToDateTime(time);
         }
+
+        public AzureBlobModel(FileInfo f, string Container, string groupName, string newName ,string  contentType)
+        {
+            long time = DateTime.Now.ToUniversalTime().Ticks - 621355968000000000;
+            ContentType = contentType;
+            ContentDisposition = "form-data; name=\"file\"; filename=\"" + f.Name + "\"";
+            FileName = f.Name;
+            // Headers = f.Headers.Values;
+            RealName = groupName + "/" + newName;
+            Folder = groupName;
+            Length = f.Length;
+            Name = "file";
+            UploadTime = time;
+            PartitionKey = Container;
+            RowKey = Guid.NewGuid().ToString();
+            Extension = f.Name.Substring(f.Name.LastIndexOf(".") + 1, (f.Name.Length - f.Name.LastIndexOf(".") - 1)); //扩展名
+            Timestamp = DateTimeHelper.ConvertToDateTime(time);
+        }
         [Required(ErrorMessage = "{0} 必须填写")]
         public string Folder { get; set; }
         [Required(ErrorMessage = "{0} 必须填写")]

+ 44 - 0
TEAMModelOS.SDK/Module/AzureBlob/Implements/AzureBlobDBRepository.cs

@@ -16,6 +16,7 @@ using System.Linq;
 using Microsoft.WindowsAzure.Storage.Shared.Protocol;
 using TEAMModelOS.SDK.Extension.SnowFlake;
 using TEAMModelOS.SDK.Context.Constant;
+using Microsoft.AspNetCore.StaticFiles;
 
 namespace TEAMModelOS.SDK.Module.AzureBlob.Implements
 {
@@ -133,6 +134,49 @@ namespace TEAMModelOS.SDK.Module.AzureBlob.Implements
             }
             return list;
         }
+        public async Task<AzureBlobModel> UploadPath(string path, string fileSpace = "common") {
+            string groupName = fileSpace + "/" + DateTime.Now.ToString("yyyyMMdd");
+            string newFileName = DateTime.Now.ToString("HHmmssfffffff");
+            blobContainer = blobClient.GetContainerReference(groupName);
+            StorageUri url = blobContainer.StorageUri;
+            FileInfo file = new FileInfo(path);
+            string[] names = file.Name.Split(".");
+            string name = "";
+            for (int i = 0; i < names.Length - 1; i++)
+            {
+                name = name + names[i];
+            }
+            if (names.Length <= 1)
+            {
+                name = file.Name + "_" + newFileName;
+            }
+            else
+            {
+                name = name + "_" + newFileName + "." + names[names.Length - 1];
+            }
+            string fileext = file.Name.Substring(file.Name.LastIndexOf(".") > 0 ? file.Name.LastIndexOf(".") : 0);
+
+            var parsedContentDisposition = ContentDispositionHeaderValue.Parse("form-data; name=\"files\"; filename=\"" + file.Name + "\"");
+            var blockBlob = blobContainer.GetBlockBlobReference(name);
+            ContentTypeDict.dict.TryGetValue(fileext, out string content_type);
+            if (!string.IsNullOrEmpty(content_type))
+            {
+                blockBlob.Properties.ContentType = content_type;
+            }
+            else {
+                content_type = "application/octet-stream";
+                blockBlob.Properties.ContentType = content_type;
+            }
+            await blockBlob.UploadFromFileAsync(path);
+            //var provider = new FileExtensionContentTypeProvider();
+            //var memi = provider.Mappings[fileext];
+            AzureBlobModel model = new AzureBlobModel(file, _options.Container, groupName, name , content_type)
+            {
+                BlobUrl = url.PrimaryUri.ToString().Split("?")[0] + "/" + name
+            };
+            return model;
+        }
+
 
         public async Task<AzureBlobModel> UploadWord(IFormFile file, string fileSpace = "wordfiles")
         {

+ 1 - 0
TEAMModelOS.SDK/Module/AzureBlob/Interfaces/IAzureBlobDBRepository.cs

@@ -9,5 +9,6 @@ namespace TEAMModelOS.SDK.Module.AzureBlob.Interfaces
     {
         Task<List<AzureBlobModel>> UploadFiles(IFormFile[] file ,string fileSpace="common");
         Task<AzureBlobModel> UploadWord(IFormFile file, string fileSpace = "wordfiles");
+        Task<AzureBlobModel> UploadPath(string path, string fileSpace = "common");
     }
 }

+ 602 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/ChartUpdater.cs

@@ -0,0 +1,602 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+using OpenXmlPowerTools;
+
+namespace OpenXmlPowerTools
+{
+    public enum ChartDataType
+    {
+        Number,
+        String,
+        DateTime,
+    }
+
+    // Format Codes
+    // 0 - general
+    // 1 - 0
+    // 2 - 0.00
+    // 3 - #,##0
+    // 4 - #,##0.00
+    // 9 - 0%
+    // 10 - 0.00%
+    // 11 - 0.00E+00
+    // 12 - # ?/?
+    // 13 - # ??/??
+    // 14 - mm-dd-yy
+    // 15 - d-mmm-yy
+    // 16 - d-mmm
+    // 17 - mmm-yy
+    // 18 - h:mm AM/PM
+    // 19 - h:mm:ss AM/PM
+    // 20 - h:mm
+    // 21 - h:mm:ss
+    // 22 - m/d/yy h:mm
+    // 37 - #,##0 ;(#,##0)
+    // 38 - #,##0 ;[Red](#,##0)
+    // 39 - #,##0.00;(#,##0.00)
+    // 40 - #,##0.00;[Red](#,##0.00)
+    // 45 - mm:ss
+    // 46 - [h]:mm:ss
+    // 47 - mmss.0
+    // 48 - ##0.0E+0
+    // 49 - @
+
+    public class ChartData
+    {
+        public string[] SeriesNames;
+
+        public ChartDataType CategoryDataType;
+        public int CategoryFormatCode;
+        public string[] CategoryNames;
+
+        public double[][] Values;
+    }
+
+    public class ChartUpdater
+    {
+        public static bool UpdateChart(WordprocessingDocument wDoc, string contentControlTag, ChartData chartData)
+        {
+            var mainDocumentPart = wDoc.MainDocumentPart;
+            var mdXDoc = mainDocumentPart.GetXDocument();
+            var cc = mdXDoc.Descendants(W.sdt)
+                .FirstOrDefault(sdt => (string)sdt.Elements(W.sdtPr).Elements(W.tag).Attributes(W.val).FirstOrDefault() == contentControlTag);
+            if (cc != null)
+            {
+                var chartRid = (string)cc.Descendants(C.chart).Attributes(R.id).FirstOrDefault();
+                if (chartRid != null)
+                {
+                    ChartPart chartPart = (ChartPart)mainDocumentPart.GetPartById(chartRid);
+                    UpdateChart(chartPart, chartData);
+                    var newContent = cc.Elements(W.sdtContent).Elements().Select(e => new XElement(e));
+                    cc.ReplaceWith(newContent);
+                    mainDocumentPart.PutXDocument();
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public static void UpdateChart(ChartPart chartPart, ChartData chartData)
+        {
+            if (chartData.Values.Length != chartData.SeriesNames.Length)
+                throw new ArgumentException("Invalid chart data");
+            foreach (var ser in chartData.Values)
+            {
+                if (ser.Length != chartData.CategoryNames.Length)
+                    throw new ArgumentException("Invalid chart data");
+            }
+
+            UpdateSeries(chartPart, chartData);
+        }
+
+        private static Dictionary<int, string> FormatCodes = new Dictionary<int, string>()
+        {
+            { 0, "general" },
+            { 1, "0" },
+            { 2, "0.00" },
+            { 3, "#,##0" },
+            { 4, "#,##0.00" },
+            { 9, "0%" },
+            { 10, "0.00%" },
+            { 11, "0.00E+00" },
+            { 12, "# ?/?" },
+            { 13, "# ??/??" },
+            { 14, "mm-dd-yy" },
+            { 15, "d-mmm-yy" },
+            { 16, "d-mmm" },
+            { 17, "mmm-yy" },
+            { 18, "h:mm AM/PM" },
+            { 19, "h:mm:ss AM/PM" },
+            { 20, "h:mm" },
+            { 21, "h:mm:ss" },
+            { 22, "m/d/yy h:mm" },
+            { 37, "#,##0 ;(#,##0)" },
+            { 38, "#,##0 ;[Red](#,##0)" },
+            { 39, "#,##0.00;(#,##0.00)" },
+            { 40, "#,##0.00;[Red](#,##0.00)" },
+            { 45, "mm:ss" },
+            { 46, "[h]:mm:ss" },
+            { 47, "mmss.0" },
+            { 48, "##0.0E+0" },
+            { 49, "@" },
+        };
+
+        private static void UpdateSeries(ChartPart chartPart, ChartData chartData)
+        {
+            UpdateEmbeddedWorkbook(chartPart, chartData);
+
+            XDocument cpXDoc = chartPart.GetXDocument();
+            XElement root = cpXDoc.Root;
+            var firstSeries = root.Descendants(C.ser).FirstOrDefault();
+            var numRef = firstSeries.Elements(C.val).Elements(C.numRef).FirstOrDefault();
+            string sheetName = null;
+            var f = (string)firstSeries.Descendants(C.f).FirstOrDefault();
+            if (f != null)
+                sheetName = f.Split('!')[0];
+
+            // remove all but first series
+            XName chartType = firstSeries.Parent.Name;
+            firstSeries.Parent.Elements(C.ser).Skip(1).Remove();
+
+            var newSetOfSeries = chartData.SeriesNames
+                .Select((string sn, int si) =>
+                {
+                    XElement cat = null;
+
+                    var oldCat = firstSeries.Elements(C.cat).FirstOrDefault();
+                    if (oldCat == null)
+                        throw new OpenXmlPowerToolsException("Invalid chart markup");
+
+                    var catHasFormula = oldCat.Descendants(C.f).Any();
+                    if (catHasFormula)
+                    {
+                        XElement newFormula = null;
+                        if (sheetName != null)
+                            newFormula = new XElement(C.f, string.Format("{0}!$A$2:$A${1}", sheetName, chartData.CategoryNames.Length + 1));
+                        if (chartData.CategoryDataType == ChartDataType.String)
+                        {
+                            cat = new XElement(C.cat,
+                                new XElement(C.strRef,
+                                    newFormula,
+                                    new XElement(C.strCache,
+                                        new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                        chartData.CategoryNames.Select((string cn, int ci) =>
+                                        {
+                                            var newPt = new XElement(C.pt,
+                                                new XAttribute("idx", ci),
+                                                new XElement(C.v, chartData.CategoryNames[ci]));
+                                            return newPt;
+                                        }))));
+                        }
+                        else
+                        {
+                            cat = new XElement(C.cat,
+                                new XElement(C.numRef,
+                                    newFormula,
+                                    new XElement(C.numCache,
+                                        new XElement(C.formatCode, FormatCodes[chartData.CategoryFormatCode]),
+                                        new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                        chartData.CategoryNames.Select((string cn, int ci) =>
+                                        {
+                                            var newPt = new XElement(C.pt,
+                                                new XAttribute("idx", ci),
+                                                new XElement(C.v, chartData.CategoryNames[ci]));
+                                            return newPt;
+                                        }))));
+                        }
+                    }
+                    else
+                    {
+                        if (chartData.CategoryDataType == ChartDataType.String)
+                        {
+                            cat = new XElement(C.cat,
+                                new XElement(C.strLit,
+                                    new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                    chartData.CategoryNames.Select((string cn, int ci) =>
+                                    {
+                                        var newPt = new XElement(C.pt,
+                                            new XAttribute("idx", ci),
+                                            new XElement(C.v, chartData.CategoryNames[ci]));
+                                        return newPt;
+                                    })));
+                        }
+                        else
+                        {
+                            cat = new XElement(C.cat,
+                                new XElement(C.numLit,
+                                    new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                    chartData.CategoryNames.Select((string cn, int ci) =>
+                                    {
+                                        var newPt = new XElement(C.pt,
+                                            new XAttribute("idx", ci),
+                                            new XElement(C.v, chartData.CategoryNames[ci]));
+                                        return newPt;
+                                    })));
+                        }
+                    }
+
+                    XElement newCval = null;
+
+                    if (sheetName == null)
+                    {
+                        newCval = new XElement(C.val,
+                                new XElement(C.numLit,
+                                    new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                    chartData.CategoryNames.Select((string cn, int ci) =>
+                                    {
+                                        var newPt = new XElement(C.pt,
+                                            new XAttribute("idx", ci),
+                                            new XElement(C.v, chartData.Values[si][ci]));
+                                        return newPt;
+                                    })));
+                    }
+                    else
+                    {
+                        newCval = new XElement(C.val,
+                                new XElement(C.numRef,
+                                    sheetName != null ?
+                                        new XElement(C.f, string.Format("{0}!${2}$2:${2}${1}", sheetName, chartData.CategoryNames.Length + 1, SpreadsheetMLUtil.IntToColumnId(si + 1))) : null,
+                                    new XElement(C.numCache,
+                                        sheetName != null ? numRef.Descendants(C.formatCode) : null,
+                                        new XElement(C.ptCount, new XAttribute("val", chartData.CategoryNames.Length)),
+                                        chartData.CategoryNames.Select((string cn, int ci) =>
+                                        {
+                                            var newPt = new XElement(C.pt,
+                                                new XAttribute("idx", ci),
+                                                new XElement(C.v, chartData.Values[si][ci]));
+                                            return newPt;
+                                        }))));
+                    }
+
+                    var serHasFormula = firstSeries.Descendants(C.f).Any();
+                    XElement tx = null;
+                    if (serHasFormula)
+                    {
+                        XElement newFormula = null;
+                        if (sheetName != null)
+                            newFormula = new XElement(C.f, string.Format("{0}!${1}$1", sheetName, SpreadsheetMLUtil.IntToColumnId(si + 1)));
+                        tx = new XElement(C.tx,
+                            new XElement(C.strRef,
+                                newFormula,
+                                new XElement(C.strCache,
+                                    new XElement(C.ptCount, new XAttribute("val", 1)),
+                                    new XElement(C.pt,
+                                        new XAttribute("idx", 0),
+                                        new XElement(C.v, chartData.SeriesNames[si])))));
+                    }
+                    else
+                    {
+                        tx = new XElement(C.tx,
+                            new XElement(C.v, chartData.SeriesNames[si]));
+                    }
+
+                    XElement newSer = null;
+
+                    if (chartType == C.area3DChart || chartType == C.areaChart)
+                    {
+                        newSer = new XElement(C.ser,
+                            // common
+                            new XElement(C.idx, new XAttribute("val", si)),
+                            new XElement(C.order, new XAttribute("val", si)),
+                            tx,
+                            firstSeries.Elements(C.spPr),
+
+                            // CT_AreaSer
+                            firstSeries.Elements(C.pictureOptions),
+                            firstSeries.Elements(C.dPt),
+                            firstSeries.Elements(C.dLbls),
+                            firstSeries.Elements(C.trendline),
+                            firstSeries.Elements(C.errBars),
+                            cat,
+                            newCval,
+                            firstSeries.Elements(C.extLst));
+                    }
+                    else if (chartType == C.bar3DChart || chartType == C.barChart)
+                    {
+                        newSer = new XElement(C.ser,
+                            // common
+                            new XElement(C.idx, new XAttribute("val", si)),
+                            new XElement(C.order, new XAttribute("val", si)),
+                            tx,
+                            firstSeries.Elements(C.spPr),
+
+                            // CT_BarSer
+                            firstSeries.Elements(C.invertIfNegative),
+                            firstSeries.Elements(C.pictureOptions),
+                            firstSeries.Elements(C.dPt),
+                            firstSeries.Elements(C.dLbls),
+                            firstSeries.Elements(C.trendline),
+                            firstSeries.Elements(C.errBars),
+                            cat,
+                            newCval,
+                            firstSeries.Elements(C.shape),
+                            firstSeries.Elements(C.extLst));
+                    }
+                    else if (chartType == C.line3DChart || chartType == C.lineChart || chartType == C.stockChart)
+                    {
+                        newSer = new XElement(C.ser,
+                            // common
+                            new XElement(C.idx, new XAttribute("val", si)),
+                            new XElement(C.order, new XAttribute("val", si)),
+                            tx,
+                            firstSeries.Elements(C.spPr),
+
+                            // CT_LineSer
+                            firstSeries.Elements(C.marker),
+                            firstSeries.Elements(C.dPt),
+                            firstSeries.Elements(C.dLbls),
+                            firstSeries.Elements(C.trendline),
+                            firstSeries.Elements(C.errBars),
+                            cat,
+                            newCval,
+                            firstSeries.Elements(C.smooth),
+                            firstSeries.Elements(C.extLst));
+                    }
+                    else if (chartType == C.doughnutChart || chartType == C.ofPieChart || chartType == C.pie3DChart || chartType == C.pieChart)
+                    {
+                        newSer = new XElement(C.ser,
+                            // common
+                            new XElement(C.idx, new XAttribute("val", si)),
+                            new XElement(C.order, new XAttribute("val", si)),
+                            tx,
+                            firstSeries.Elements(C.spPr),
+
+                            // CT_PieSer
+                            firstSeries.Elements(C.explosion),
+                            firstSeries.Elements(C.dPt),
+                            firstSeries.Elements(C.dLbls),
+                            cat,
+                            newCval,
+                            firstSeries.Elements(C.extLst));
+                    }
+                    else if (chartType == C.surface3DChart || chartType == C.surfaceChart)
+                    {
+                        newSer = new XElement(C.ser,
+                            // common
+                            new XElement(C.idx, new XAttribute("val", si)),
+                            new XElement(C.order, new XAttribute("val", si)),
+                            tx,
+                            firstSeries.Elements(C.spPr),
+
+                            // CT_SurfaceSer
+                            cat,
+                            newCval,
+                            firstSeries.Elements(C.extLst));
+                    }
+
+                    if (newSer == null)
+                        throw new OpenXmlPowerToolsException("Unsupported chart type");
+
+                    int accentNumber = (si % 6) + 1;
+                    newSer = (XElement)UpdateAccentTransform(newSer, accentNumber);
+                    return newSer;
+                });
+            firstSeries.ReplaceWith(newSetOfSeries);
+            chartPart.PutXDocument();
+        }
+
+        private static void UpdateEmbeddedWorkbook(ChartPart chartPart, ChartData chartData)
+        {
+            XDocument cpXDoc = chartPart.GetXDocument();
+            XElement root = cpXDoc.Root;
+            var firstSeries = root.Descendants(C.ser).FirstOrDefault();
+            if (firstSeries == null)
+                return;
+            var firstFormula = (string)firstSeries.Descendants(C.f).FirstOrDefault();
+            if (firstFormula == null)
+                return;
+            var sheet = firstFormula.Split('!')[0];
+            var embeddedSpreadsheetRid = (string)root.Descendants(C.externalData).Attributes(R.id).FirstOrDefault();
+            if (embeddedSpreadsheetRid == null)
+                return;
+            var embeddedSpreadsheet = chartPart.GetPartById(embeddedSpreadsheetRid);
+            if (embeddedSpreadsheet != null)
+            {
+                using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(embeddedSpreadsheet.GetStream(), true))
+                {
+                    var workbookPart = sDoc.WorkbookPart;
+                    var wbRoot = workbookPart.GetXDocument().Root;
+                    var sheetRid = (string)wbRoot
+                        .Elements(S.sheets)
+                        .Elements(S.sheet)
+                        .Where(s => (string)s.Attribute("name") == sheet)
+                        .Attributes(R.id)
+                        .FirstOrDefault();
+                    if (sheetRid != null)
+                    {
+                        var sheetPart = workbookPart.GetPartById(sheetRid);
+                        var xdSheet = sheetPart.GetXDocument();
+                        var sheetData = xdSheet.Descendants(S.sheetData).FirstOrDefault();
+
+                        var stylePart = workbookPart.WorkbookStylesPart;
+                        var xdStyle = stylePart.GetXDocument();
+
+                        int categoryStyleId = 0;
+                        if (chartData.CategoryFormatCode != 0)
+                            categoryStyleId = AddDxfToDxfs(xdSheet, xdStyle, chartData.CategoryFormatCode);
+                        stylePart.PutXDocument();
+
+                        var firstRow = new XElement(S.row,
+                            new XAttribute("r", "1"),
+                            new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)),
+                            new [] { new XElement(S.c,
+                                new XAttribute("r", "A1"),
+                                new XAttribute("t", "str"),
+                                new XElement(S.v,
+                                    new XAttribute(XNamespace.Xml + "space", "preserve"),
+                                    " "))}
+                                .Concat(
+                                    chartData.SeriesNames
+                                        .Select((sn, i) => new XElement(S.c,
+                                            new XAttribute("r", RowColToString(0, i + 1)),
+                                            new XAttribute("t", "str"),
+                                            new XElement(S.v, sn)))));
+                        var otherRows = chartData
+                            .CategoryNames
+                            .Select((cn, r) =>
+                            {
+                                var row = new XElement(S.row,
+                                    new XAttribute("r", r + 2),
+                                    new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)),
+                                    new[] {
+                                        new XElement(S.c,
+                                            new XAttribute("r", RowColToString(r + 1, 0)),
+                                            categoryStyleId != 0 ? new XAttribute("s", categoryStyleId) : null,
+                                            chartData.CategoryDataType == ChartDataType.String ? new XAttribute("t", "str") : null,
+                                            new XElement(S.v, cn))
+                                    }.Concat(
+                                        Enumerable.Range(0, chartData.Values.Length)
+                                            .Select((c, ci) =>
+                                            {
+                                                var cell = new XElement(S.c,
+                                                    new XAttribute("r", RowColToString(r + 1, ci + 1)),
+                                                    new XElement(S.v, chartData.Values[ci][r]));
+                                                return cell;
+                                            })));
+                                return row;
+                            });
+                        var allRows = new[] {
+                            firstRow
+                        }.Concat(otherRows);
+                        var newSheetData = new XElement(S.sheetData,
+                            allRows);
+                        sheetData.ReplaceWith(newSheetData);
+                        sheetPart.PutXDocument();
+
+                        var tablePartRid = (string)xdSheet
+                            .Root
+                            .Elements(S.tableParts)
+                            .Elements(S.tablePart)
+                            .Attributes(R.id)
+                            .FirstOrDefault();
+                        if (tablePartRid != null)
+                        {
+                            var partTable = sheetPart.GetPartById(tablePartRid);
+                            var xdTablePart = partTable.GetXDocument();
+                            var xaRef = xdTablePart.Root.Attribute("ref");
+                            xaRef.Value = string.Format("A1:{0}", RowColToString(chartData.CategoryNames.Length - 1, chartData.SeriesNames.Length));
+                            var xeNewTableColumns = new XElement(S.tableColumns,
+                                new XAttribute("count", chartData.SeriesNames.Count() + 1),
+                                new[] {
+                                    new XElement(S.tableColumn,
+                                        new XAttribute("id", 1),
+                                        new XAttribute("name", " "))
+                                }.Concat(
+                                    chartData.SeriesNames.Select((cn, ci) =>
+                                        new XElement(S.tableColumn,
+                                            new XAttribute("id", ci + 2),
+                                            new XAttribute("name", cn)))));
+                            var xeExistingTableColumns = xdTablePart.Root.Element(S.tableColumns);
+                            if (xeExistingTableColumns != null)
+                                xeExistingTableColumns.ReplaceWith(xeNewTableColumns);
+                            partTable.PutXDocument();
+                        }
+                    }
+                }
+            }
+        }
+
+        private static int AddDxfToDxfs(XDocument xdSheet, XDocument xdStyle, int formatCodeToAdd)
+        {
+            // add xf to cellXfs
+            var cellXfs = xdStyle
+                .Root
+                .Element(S.cellXfs);
+            if (cellXfs == null)
+            {
+                var cellStyleXfs = xdStyle
+                    .Root
+                    .Element(S.cellStyleXfs);
+                if (cellStyleXfs != null)
+                {
+                    cellStyleXfs.AddAfterSelf(
+                        new XElement(S.cellXfs,
+                            new XAttribute("count", 0)));
+                    cellXfs = xdSheet
+                        .Root
+                        .Element(S.cellXfs);
+                }
+            }
+            if (cellXfs == null)
+            {
+                var borders = xdStyle
+                    .Root
+                    .Element(S.borders);
+                if (borders != null)
+                {
+                    borders.AddAfterSelf(
+                        new XElement(S.cellXfs,
+                            new XAttribute("count", 0)));
+                    cellXfs = xdSheet
+                        .Root
+                        .Element(S.cellXfs);
+                }
+            }
+            if (cellXfs == null)
+                throw new OpenXmlPowerToolsException("Internal error");
+
+            var cnt = (int)cellXfs.Attribute("count");
+            cnt++;
+            cellXfs.Attribute("count").Value = cnt.ToString();
+            cellXfs.Add(
+                new XElement(S.xf,
+                    new XAttribute("numFmtId", formatCodeToAdd),
+                    new XAttribute("fontId", 0),
+                    new XAttribute("fillId", 0),
+                    new XAttribute("borderId", 0),
+                    new XAttribute("applyNumberFormat", 1)));
+            return cnt - 1;
+        }
+
+        private static string RowColToString(int row, int col)
+        {
+            var str = SpreadsheetMLUtil.IntToColumnId(col) + (row + 1).ToString();
+            return str;
+        }
+
+        private static object UpdateAccentTransform(XNode node, int accentNumber)
+        {
+            XElement element = node as XElement;
+            if (element != null)
+            {
+                if (element.Name == A.schemeClr && (string)element.Attribute("val") == "accent1")
+                    return new XElement(A.schemeClr, new XAttribute("val", "accent" + accentNumber));
+
+                return new XElement(element.Name,
+                    element.Attributes(),
+                    element.Nodes().Select(n => UpdateAccentTransform(n, accentNumber)));
+            }
+            return node;
+        }
+
+        public static bool UpdateChart(PresentationDocument pDoc, int slideNumber, ChartData chartData)
+        {
+            var presentationPart = pDoc.PresentationPart;
+            var pXDoc = presentationPart.GetXDocument();
+            var sldIdElement = pXDoc.Root.Elements(P.sldIdLst).Elements(P.sldId).Skip(slideNumber - 1).FirstOrDefault();
+            if (sldIdElement != null)
+            {
+                var rId = (string)sldIdElement.Attribute(R.id);
+                var slidePart = presentationPart.GetPartById(rId);
+                var sXDoc = slidePart.GetXDocument();
+                var chartRid = (string)sXDoc.Descendants(C.chart).Attributes(R.id).FirstOrDefault();
+                if (chartRid != null)
+                {
+                    ChartPart chartPart = (ChartPart)slidePart.GetPartById(chartRid);
+                    UpdateChart(chartPart, chartData);
+                    return true;
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+}

+ 36 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/ColorParser.cs

@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+
+namespace OpenXmlPowerTools
+{
+    public static class ColorParser
+    {
+        public static Color FromName(string name)
+        {
+            return Color.FromName(name);
+        }
+
+        public static bool TryFromName(string name, out Color color)
+        {
+            try
+            {
+                color = Color.FromName(name);
+
+                return color.IsNamedColor;
+            }
+            catch
+            {
+                color = default(Color);
+
+                return false;
+            }
+        }
+
+        public static bool IsValidName(string name)
+        {
+            return TryFromName(name, out _);
+        }
+    }
+}

+ 67 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnit.cs

@@ -0,0 +1,67 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    public abstract class ComparisonUnit
+    {
+        private int? _descendantContentAtomsCount;
+
+        public CorrelationStatus CorrelationStatus { get; set; }
+
+        public List<ComparisonUnit> Contents { get; protected set; }
+
+        public string SHA1Hash { get; protected set; }
+
+        public int DescendantContentAtomsCount
+        {
+            get
+            {
+                if (_descendantContentAtomsCount != null) return (int) _descendantContentAtomsCount;
+
+                _descendantContentAtomsCount = DescendantContentAtoms().Count();
+                return (int) _descendantContentAtomsCount;
+            }
+        }
+
+        private IEnumerable<ComparisonUnit> Descendants()
+        {
+            var comparisonUnitList = new List<ComparisonUnit>();
+            DescendantsInternal(this, comparisonUnitList);
+            return comparisonUnitList;
+        }
+
+        public IEnumerable<ComparisonUnitAtom> DescendantContentAtoms()
+        {
+            return Descendants().OfType<ComparisonUnitAtom>();
+        }
+
+        private static void DescendantsInternal(
+            ComparisonUnit comparisonUnit,
+            List<ComparisonUnit> comparisonUnitList)
+        {
+            foreach (ComparisonUnit cu in comparisonUnit.Contents)
+            {
+                comparisonUnitList.Add(cu);
+                if (cu.Contents != null && cu.Contents.Any())
+                    DescendantsInternal(cu, comparisonUnitList);
+            }
+        }
+
+        public abstract string ToString(int indent);
+
+        internal static string ComparisonUnitListToString(ComparisonUnit[] cul)
+        {
+            var sb = new StringBuilder();
+            sb.Append("Dump Comparision Unit List To String" + Environment.NewLine);
+            foreach (ComparisonUnit item in cul) sb.Append(item.ToString(2) + Environment.NewLine);
+
+            return sb.ToString();
+        }
+    }
+}

+ 215 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitAtom.cs

@@ -0,0 +1,215 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public class ComparisonUnitAtom : ComparisonUnit
+    {
+        public ComparisonUnitAtom(
+            XElement contentElement,
+            XElement[] ancestorElements,
+            OpenXmlPart part,
+            WmlComparerSettings settings)
+        {
+            ContentElement = contentElement;
+            AncestorElements = ancestorElements;
+            Part = part;
+            RevTrackElement = GetRevisionTrackingElementFromAncestors(contentElement, AncestorElements);
+
+            if (RevTrackElement == null)
+            {
+                CorrelationStatus = CorrelationStatus.Equal;
+            }
+            else
+            {
+                if (RevTrackElement.Name == W.del)
+                {
+                    CorrelationStatus = CorrelationStatus.Deleted;
+                }
+                else if (RevTrackElement.Name == W.ins)
+                {
+                    CorrelationStatus = CorrelationStatus.Inserted;
+                }
+            }
+
+            var sha1Hash = (string) contentElement.Attribute(PtOpenXml.SHA1Hash);
+            if (sha1Hash != null)
+            {
+                SHA1Hash = sha1Hash;
+            }
+            else
+            {
+                string shaHashString = GetSha1HashStringForElement(ContentElement, settings);
+                SHA1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(shaHashString);
+            }
+        }
+
+        // AncestorElements are kept in order from the body to the leaf, because this is the order in which we need to access in order
+        // to reassemble the document.  However, in many places in the code, it is necessary to find the nearest ancestor, i.e. cell
+        // so it is necessary to reverse the order when looking for it, i.e. look from the leaf back to the body element.
+
+        public XElement[] AncestorElements { get; }
+
+        public XElement ContentElement { get; }
+
+        public XElement RevTrackElement { get; }
+
+        public string[] AncestorUnids { get; set; }
+
+        public ComparisonUnitAtom ComparisonUnitAtomBefore { get; set; }
+
+        public XElement ContentElementBefore { get; set; }
+
+        public OpenXmlPart Part { get; }
+
+        private static string GetSha1HashStringForElement(XElement contentElement, WmlComparerSettings settings)
+        {
+            string text = contentElement.Value;
+            if (settings.CaseInsensitive)
+            {
+                text = text.ToUpper(settings.CultureInfo);
+            }
+
+            return contentElement.Name.LocalName + text;
+        }
+
+        private static XElement GetRevisionTrackingElementFromAncestors(
+            XElement contentElement,
+            IEnumerable<XElement> ancestors)
+        {
+            return contentElement.Name == W.pPr
+                ? contentElement.Elements(W.rPr).Elements().FirstOrDefault(e => e.Name == W.del || e.Name == W.ins)
+                : ancestors.FirstOrDefault(a => a.Name == W.del || a.Name == W.ins);
+        }
+
+        public override string ToString()
+        {
+            return ToString(0);
+        }
+
+        public override string ToString(int indent)
+        {
+            const int xNamePad = 16;
+            string indentString = "".PadRight(indent);
+
+            var sb = new StringBuilder();
+            sb.Append(indentString);
+
+            var correlationStatus = "";
+            if (CorrelationStatus != CorrelationStatus.Nil)
+            {
+                correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] ";
+            }
+
+            if (ContentElement.Name == W.t || ContentElement.Name == W.delText)
+            {
+                sb.AppendFormat(
+                    "Atom {0}: {1} {2} SHA1:{3} ",
+                    PadLocalName(xNamePad, this),
+                    ContentElement.Value,
+                    correlationStatus,
+                    SHA1Hash.Substring(0, 8));
+
+                AppendAncestorsDump(sb, this);
+            }
+            else
+            {
+                sb.AppendFormat(
+                    "Atom {0}:   {1} SHA1:{2} ",
+                    PadLocalName(xNamePad, this),
+                    correlationStatus,
+                    SHA1Hash.Substring(0, 8));
+
+                AppendAncestorsDump(sb, this);
+            }
+
+            return sb.ToString();
+        }
+
+        public string ToStringAncestorUnids()
+        {
+            return ToStringAncestorUnids(0);
+        }
+
+        private string ToStringAncestorUnids(int indent)
+        {
+            const int xNamePad = 16;
+            string indentString = "".PadRight(indent);
+
+            var sb = new StringBuilder();
+            sb.Append(indentString);
+
+            var correlationStatus = "";
+            if (CorrelationStatus != CorrelationStatus.Nil)
+            {
+                correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] ";
+            }
+
+            if (ContentElement.Name == W.t || ContentElement.Name == W.delText)
+            {
+                sb.AppendFormat(
+                    "Atom {0}: {1} {2} SHA1:{3} ",
+                    PadLocalName(xNamePad, this),
+                    ContentElement.Value,
+                    correlationStatus,
+                    SHA1Hash.Substring(0, 8));
+
+                AppendAncestorsUnidsDump(sb, this);
+            }
+            else
+            {
+                sb.AppendFormat(
+                    "Atom {0}:   {1} SHA1:{2} ",
+                    PadLocalName(xNamePad, this),
+                    correlationStatus,
+                    SHA1Hash.Substring(0, 8));
+
+                AppendAncestorsUnidsDump(sb, this);
+            }
+
+            return sb.ToString();
+        }
+
+        private static string PadLocalName(int xNamePad, ComparisonUnitAtom item)
+        {
+            return (item.ContentElement.Name.LocalName + " ").PadRight(xNamePad, '-') + " ";
+        }
+
+        private static void AppendAncestorsDump(StringBuilder sb, ComparisonUnitAtom sr)
+        {
+            string s = sr
+                .AncestorElements.Select(p => p.Name.LocalName + GetUnid(p) + "/")
+                .StringConcatenate()
+                .TrimEnd('/');
+
+            sb.Append("Ancestors:" + s);
+        }
+
+        private static void AppendAncestorsUnidsDump(StringBuilder sb, ComparisonUnitAtom sr)
+        {
+            var zipped = sr.AncestorElements.Zip(sr.AncestorUnids, (a, u) => new
+            {
+                AncestorElement = a,
+                AncestorUnid = u
+            });
+
+            string s = zipped
+                .Select(p => p.AncestorElement.Name.LocalName + "[" + p.AncestorUnid.Substring(0, 8) + "]/")
+                .StringConcatenate().TrimEnd('/');
+
+            sb.Append("Ancestors:" + s);
+        }
+
+        private static string GetUnid(XElement p)
+        {
+            var unid = (string) p.Attribute(PtOpenXml.Unid);
+            return unid == null ? "" : "[" + unid.Substring(0, 8) + "]";
+        }
+    }
+}

+ 77 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitGroup.cs

@@ -0,0 +1,77 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    internal class ComparisonUnitGroup : ComparisonUnit
+    {
+        public ComparisonUnitGroup(
+            IEnumerable<ComparisonUnit> comparisonUnitList,
+            ComparisonUnitGroupType groupType,
+            int level)
+        {
+            Contents = comparisonUnitList.ToList();
+            ComparisonUnitGroupType = groupType;
+            ComparisonUnit first = Contents.First();
+            ComparisonUnitAtom comparisonUnitAtom = GetFirstComparisonUnitAtomOfGroup(first);
+
+            XElement[] ancestorsToLookAt = comparisonUnitAtom
+                .AncestorElements
+                .Where(e => e.Name == W.tbl || e.Name == W.tr || e.Name == W.tc || e.Name == W.p || e.Name == W.txbxContent)
+                .ToArray();
+
+            XElement ancestor = ancestorsToLookAt[level];
+            if (ancestor == null) throw new OpenXmlPowerToolsException("Internal error: ComparisonUnitGroup");
+
+            SHA1Hash = (string) ancestor.Attribute(PtOpenXml.SHA1Hash);
+            CorrelatedSHA1Hash = (string) ancestor.Attribute(PtOpenXml.CorrelatedSHA1Hash);
+            StructureSHA1Hash = (string) ancestor.Attribute(PtOpenXml.StructureSHA1Hash);
+        }
+
+        public ComparisonUnitGroupType ComparisonUnitGroupType { get; }
+
+        public string CorrelatedSHA1Hash { get; }
+
+        public string StructureSHA1Hash { get; }
+
+        private static ComparisonUnitAtom GetFirstComparisonUnitAtomOfGroup(ComparisonUnit group)
+        {
+            ComparisonUnit thisGroup = group;
+            while (true)
+            {
+                if (thisGroup is ComparisonUnitGroup tg)
+                {
+                    thisGroup = tg.Contents.First();
+                    continue;
+                }
+
+                if (!(thisGroup is ComparisonUnitWord tw))
+                {
+                    throw new OpenXmlPowerToolsException("Internal error: GetFirstComparisonUnitAtomOfGroup");
+                }
+
+                var ca = (ComparisonUnitAtom) tw.Contents.First();
+                return ca;
+            }
+        }
+
+        public override string ToString(int indent)
+        {
+            var sb = new StringBuilder();
+            sb.Append("".PadRight(indent) + "Group Type: " + ComparisonUnitGroupType + " SHA1:" + SHA1Hash + Environment.NewLine);
+
+            foreach (ComparisonUnit comparisonUnitAtom in Contents)
+            {
+                sb.Append(comparisonUnitAtom.ToString(indent + 2));
+            }
+
+            return sb.ToString();
+        }
+    }
+}

+ 14 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitGroupType.cs

@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace OpenXmlPowerTools
+{
+    internal enum ComparisonUnitGroupType
+    {
+        Paragraph,
+        Table,
+        Row,
+        Cell,
+        Textbox,
+    };
+}

+ 81 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/ComparisonUnitWord.cs

@@ -0,0 +1,81 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    internal class ComparisonUnitWord : ComparisonUnit
+    {
+        public static readonly XName[] ElementsWithRelationshipIds =
+        {
+            A.blip,
+            A.hlinkClick,
+            A.relIds,
+            C.chart,
+            C.externalData,
+            C.userShapes,
+            DGM.relIds,
+            O.OLEObject,
+            VML.fill,
+            VML.imagedata,
+            VML.stroke,
+            W.altChunk,
+            W.attachedTemplate,
+            W.control,
+            W.dataSource,
+            W.embedBold,
+            W.embedBoldItalic,
+            W.embedItalic,
+            W.embedRegular,
+            W.footerReference,
+            W.headerReference,
+            W.headerSource,
+            W.hyperlink,
+            W.printerSettings,
+            W.recipientData,
+            W.saveThroughXslt,
+            W.sourceFileName,
+            W.src,
+            W.subDoc,
+            WNE.toolbarData
+        };
+
+        public static readonly XName[] RelationshipAttributeNames =
+        {
+            R.embed,
+            R.link,
+            R.id,
+            R.cs,
+            R.dm,
+            R.lo,
+            R.qs,
+            R.href,
+            R.pict
+        };
+
+        public ComparisonUnitWord(IEnumerable<ComparisonUnitAtom> comparisonUnitAtomList)
+        {
+            Contents = comparisonUnitAtomList.OfType<ComparisonUnit>().ToList();
+            string sha1String = Contents.Select(c => c.SHA1Hash).StringConcatenate();
+            SHA1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(sha1String);
+        }
+
+        public override string ToString(int indent)
+        {
+            var sb = new StringBuilder();
+            sb.Append("".PadRight(indent) + "Word SHA1:" + SHA1Hash.Substring(0, 8) + Environment.NewLine);
+
+            foreach (ComparisonUnit comparisonUnitAtom in Contents)
+            {
+                sb.Append(comparisonUnitAtom.ToString(indent + 2) + Environment.NewLine);
+            }
+
+            return sb.ToString();
+        }
+    }
+}

+ 70 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/CorrelatedSequence.cs

@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    internal class CorrelatedSequence
+    {
+#if DEBUG
+        public string SourceFile;
+        public int SourceLine;
+#endif
+
+        public CorrelatedSequence()
+        {
+#if DEBUG
+            SourceFile = new System.Diagnostics.StackTrace(true).GetFrame(1).GetFileName();
+            SourceLine = new System.Diagnostics.StackTrace(true).GetFrame(1).GetFileLineNumber();
+#endif
+        }
+
+        public CorrelationStatus CorrelationStatus { get; set; }
+
+        // if ComparisonUnitList1 == null and ComparisonUnitList2 contains sequence, then inserted content.
+        // if ComparisonUnitList2 == null and ComparisonUnitList1 contains sequence, then deleted content.
+        // if ComparisonUnitList2 contains sequence and ComparisonUnitList1 contains sequence, then either is Unknown or Equal.
+        public ComparisonUnit[] ComparisonUnitArray1 { get; set; }
+
+        public ComparisonUnit[] ComparisonUnitArray2 { get; set; }
+
+        public override string ToString()
+        {
+            var sb = new StringBuilder();
+            const string indentString = "  ";
+            const string indentString4 = "    ";
+            sb.Append("CorrelatedSequence =====" + Environment.NewLine);
+#if DEBUG
+            sb.Append(indentString + "Created at Line: " + SourceLine + Environment.NewLine);
+#endif
+            sb.Append(indentString + "CorrelatedItem =====" + Environment.NewLine);
+            sb.Append(indentString4 + "CorrelationStatus: " + CorrelationStatus + Environment.NewLine);
+            if (CorrelationStatus == CorrelationStatus.Equal)
+            {
+                sb.Append(indentString4 + "ComparisonUnitList =====" + Environment.NewLine);
+                foreach (ComparisonUnit item in ComparisonUnitArray2)
+                    sb.Append(item.ToString(6) + Environment.NewLine);
+            }
+            else
+            {
+                if (ComparisonUnitArray1 != null)
+                {
+                    sb.Append(indentString4 + "ComparisonUnitList1 =====" + Environment.NewLine);
+                    foreach (ComparisonUnit item in ComparisonUnitArray1)
+                        sb.Append(item.ToString(6) + Environment.NewLine);
+                }
+
+                if (ComparisonUnitArray2 != null)
+                {
+                    sb.Append(indentString4 + "ComparisonUnitList2 =====" + Environment.NewLine);
+                    foreach (ComparisonUnit item in ComparisonUnitArray2)
+                        sb.Append(item.ToString(6) + Environment.NewLine);
+                }
+            }
+
+            return sb.ToString();
+        }
+    }
+}

+ 16 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/CorrelationStatus.cs

@@ -0,0 +1,16 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace OpenXmlPowerTools
+{
+    public enum CorrelationStatus
+    {
+        Nil,
+        Normal,
+        Unknown,
+        Inserted,
+        Deleted,
+        Equal,
+        Group,
+    }
+}

+ 15 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/PartSHA1HashAnnotation.cs

@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace OpenXmlPowerTools
+{
+    internal class PartSHA1HashAnnotation
+    {
+        public string Hash;
+
+        public PartSHA1HashAnnotation(string hash)
+        {
+            Hash = hash;
+        }
+    }
+}

+ 12 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WithHierarchicalGroupingKey.cs

@@ -0,0 +1,12 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace OpenXmlPowerTools
+{
+    internal class WithHierarchicalGroupingKey
+    {
+        public string[] HierarchicalGroupingArray { get; set; }
+
+        public ComparisonUnitWord ComparisonUnitWord { get; set; }
+    }
+}

+ 401 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Internal.Methods.ComparisonUnits.cs

@@ -0,0 +1,401 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        #region CreateComparisonUnitAtomList
+
+        internal static ComparisonUnitAtom[] CreateComparisonUnitAtomList(
+            OpenXmlPart part,
+            XElement contentParent,
+            WmlComparerSettings settings)
+        {
+            VerifyNoInvalidContent(contentParent);
+            AssignUnidToAllElements(contentParent); // add the Guid id to every element
+            MoveLastSectPrIntoLastParagraph(contentParent);
+            ComparisonUnitAtom[] cal = CreateComparisonUnitAtomListInternal(part, contentParent, settings).ToArray();
+
+            if (False)
+            {
+                var sb = new StringBuilder();
+                foreach (ComparisonUnitAtom item in cal)
+                    sb.Append(item + Environment.NewLine);
+                string sbs = sb.ToString();
+                TestUtil.NotePad(sbs);
+            }
+
+            return cal;
+        }
+
+        private static void VerifyNoInvalidContent(XElement contentParent)
+        {
+            XElement invalidElement = contentParent.Descendants().FirstOrDefault(d => InvalidElements.Contains(d.Name));
+            if (invalidElement == null)
+                return;
+
+            throw new NotSupportedException("Document contains " + invalidElement.Name.LocalName);
+        }
+
+        private static void MoveLastSectPrIntoLastParagraph(XElement contentParent)
+        {
+            List<XElement> lastSectPrList = contentParent.Elements(W.sectPr).ToList();
+            if (lastSectPrList.Count() > 1)
+                throw new OpenXmlPowerToolsException("Invalid document");
+
+            XElement lastSectPr = lastSectPrList.FirstOrDefault();
+            if (lastSectPr != null)
+            {
+                XElement lastParagraph = contentParent.Elements(W.p).LastOrDefault();
+                if (lastParagraph == null)
+                    throw new OpenXmlPowerToolsException("Invalid document");
+
+                XElement pPr = lastParagraph.Element(W.pPr);
+                if (pPr == null)
+                {
+                    pPr = new XElement(W.pPr);
+                    lastParagraph.AddFirst(W.pPr);
+                }
+
+                pPr.Add(lastSectPr);
+                contentParent.Elements(W.sectPr).Remove();
+            }
+        }
+
+        private static List<ComparisonUnitAtom> CreateComparisonUnitAtomListInternal(
+            OpenXmlPart part,
+            XElement contentParent,
+            WmlComparerSettings settings)
+        {
+            var comparisonUnitAtomList = new List<ComparisonUnitAtom>();
+            CreateComparisonUnitAtomListRecurse(part, contentParent, comparisonUnitAtomList, settings);
+            return comparisonUnitAtomList;
+        }
+
+        private static void CreateComparisonUnitAtomListRecurse(
+            OpenXmlPart part,
+            XElement element,
+            List<ComparisonUnitAtom> comparisonUnitAtomList,
+            WmlComparerSettings settings)
+        {
+            if (element.Name == W.body || element.Name == W.footnote || element.Name == W.endnote)
+            {
+                foreach (XElement item in element.Elements())
+                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
+                return;
+            }
+
+            if (element.Name == W.p)
+            {
+                IEnumerable<XElement> paraChildrenToProcess = element
+                    .Elements()
+                    .Where(e => e.Name != W.pPr);
+                foreach (XElement item in paraChildrenToProcess)
+                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
+                XElement paraProps = element.Element(W.pPr);
+                if (paraProps == null)
+                {
+                    var pPrComparisonUnitAtom = new ComparisonUnitAtom(
+                        new XElement(W.pPr),
+                        element.AncestorsAndSelf()
+                            .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
+                            .ToArray(),
+                        part,
+                        settings);
+                    comparisonUnitAtomList.Add(pPrComparisonUnitAtom);
+                }
+                else
+                {
+                    var pPrComparisonUnitAtom = new ComparisonUnitAtom(
+                        paraProps,
+                        element.AncestorsAndSelf()
+                            .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
+                            .ToArray(),
+                        part,
+                        settings);
+                    comparisonUnitAtomList.Add(pPrComparisonUnitAtom);
+                }
+
+                return;
+            }
+
+            if (element.Name == W.r)
+            {
+                IEnumerable<XElement> runChildrenToProcess = element
+                    .Elements()
+                    .Where(e => e.Name != W.rPr);
+                foreach (XElement item in runChildrenToProcess)
+                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
+                return;
+            }
+
+            if (element.Name == W.t || element.Name == W.delText)
+            {
+                string val = element.Value;
+                foreach (char ch in val)
+                {
+                    var sr = new ComparisonUnitAtom(
+                        new XElement(element.Name, ch),
+                        element.AncestorsAndSelf()
+                            .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
+                            .ToArray(),
+                        part,
+                        settings);
+                    comparisonUnitAtomList.Add(sr);
+                }
+
+                return;
+            }
+
+            if (AllowableRunChildren.Contains(element.Name) || element.Name == W._object)
+            {
+                var sr3 = new ComparisonUnitAtom(
+                    element,
+                    element.AncestorsAndSelf().TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes)
+                        .Reverse().ToArray(),
+                    part,
+                    settings);
+                comparisonUnitAtomList.Add(sr3);
+                return;
+            }
+
+            RecursionInfo re = RecursionElements.FirstOrDefault(z => z.ElementName == element.Name);
+            if (re != null)
+            {
+                AnnotateElementWithProps(part, element, comparisonUnitAtomList, re.ChildElementPropertyNames, settings);
+                return;
+            }
+
+            if (ElementsToThrowAway.Contains(element.Name))
+                return;
+
+            AnnotateElementWithProps(part, element, comparisonUnitAtomList, null, settings);
+        }
+
+        private static void AnnotateElementWithProps(
+            OpenXmlPart part,
+            XElement element,
+            List<ComparisonUnitAtom> comparisonUnitAtomList,
+            XName[] childElementPropertyNames,
+            WmlComparerSettings settings)
+        {
+            IEnumerable<XElement> runChildrenToProcess;
+            if (childElementPropertyNames == null)
+                runChildrenToProcess = element.Elements();
+            else
+                runChildrenToProcess = element
+                    .Elements()
+                    .Where(e => !childElementPropertyNames.Contains(e.Name));
+
+            foreach (XElement item in runChildrenToProcess)
+                CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
+        }
+
+        #endregion CreateComparisonUnitAtomList
+
+        #region GetComparisonUnitList
+
+        // The following method must be made internal if we ever turn this part of the partial class
+        // into its own class.
+        private static ComparisonUnit[] GetComparisonUnitList(
+            ComparisonUnitAtom[] comparisonUnitAtomList,
+            WmlComparerSettings settings)
+        {
+            var seed = new Atgbw
+            {
+                Key = null,
+                ComparisonUnitAtomMember = null,
+                NextIndex = 0
+            };
+
+            IEnumerable<Atgbw> groupingKey = comparisonUnitAtomList
+                .Rollup(seed, (sr, prevAtgbw, i) =>
+                {
+                    int? key;
+                    int nextIndex = prevAtgbw.NextIndex;
+                    if (sr.ContentElement.Name == W.t)
+                    {
+                        string chr = sr.ContentElement.Value;
+                        char ch = chr[0];
+                        if (ch == '.' || ch == ',')
+                        {
+                            var beforeIsDigit = false;
+                            if (i > 0)
+                            {
+                                ComparisonUnitAtom prev = comparisonUnitAtomList[i - 1];
+                                if (prev.ContentElement.Name == W.t && char.IsDigit(prev.ContentElement.Value[0]))
+                                    beforeIsDigit = true;
+                            }
+
+                            var afterIsDigit = false;
+                            if (i < comparisonUnitAtomList.Length - 1)
+                            {
+                                ComparisonUnitAtom next = comparisonUnitAtomList[i + 1];
+                                if (next.ContentElement.Name == W.t && char.IsDigit(next.ContentElement.Value[0]))
+                                    afterIsDigit = true;
+                            }
+
+                            if (beforeIsDigit || afterIsDigit)
+                            {
+                                key = nextIndex;
+                            }
+                            else
+                            {
+                                nextIndex++;
+                                key = nextIndex;
+                                nextIndex++;
+                            }
+                        }
+                        else if (settings.WordSeparators.Contains(ch))
+                        {
+                            nextIndex++;
+                            key = nextIndex;
+                            nextIndex++;
+                        }
+                        else
+                        {
+                            key = nextIndex;
+                        }
+                    }
+                    else if (WordBreakElements.Contains(sr.ContentElement.Name))
+                    {
+                        nextIndex++;
+                        key = nextIndex;
+                        nextIndex++;
+                    }
+                    else
+                    {
+                        key = nextIndex;
+                    }
+
+                    return new Atgbw
+                    {
+                        Key = key,
+                        ComparisonUnitAtomMember = sr,
+                        NextIndex = nextIndex
+                    };
+                })
+                .ToArray();
+
+            if (False)
+            {
+                var sb = new StringBuilder();
+                foreach (Atgbw item in groupingKey)
+                {
+                    sb.Append(item.Key + Environment.NewLine);
+                    sb.Append("    " + item.ComparisonUnitAtomMember.ToString(0) + Environment.NewLine);
+                }
+
+                string sbs = sb.ToString();
+                TestUtil.NotePad(sbs);
+            }
+
+            IEnumerable<IGrouping<int?, Atgbw>> groupedByWords = groupingKey
+                .GroupAdjacent(gc => gc.Key)
+                .ToArray();
+
+            if (False)
+            {
+                var sb = new StringBuilder();
+                foreach (IGrouping<int?, Atgbw> group in groupedByWords)
+                {
+                    sb.Append("Group ===== " + @group.Key + Environment.NewLine);
+                    foreach (Atgbw gc in @group)
+                    {
+                        sb.Append("    " + gc.ComparisonUnitAtomMember.ToString(0) + Environment.NewLine);
+                    }
+                }
+
+                string sbs = sb.ToString();
+                TestUtil.NotePad(sbs);
+            }
+
+            WithHierarchicalGroupingKey[] withHierarchicalGroupingKey = groupedByWords
+                .Select(g =>
+                    {
+                        string[] hierarchicalGroupingArray = g
+                            .First()
+                            .ComparisonUnitAtomMember
+                            .AncestorElements
+                            .Where(a => ComparisonGroupingElements.Contains(a.Name))
+                            .Select(a => a.Name.LocalName + ":" + (string) a.Attribute(PtOpenXml.Unid))
+                            .ToArray();
+
+                        return new WithHierarchicalGroupingKey
+                        {
+                            ComparisonUnitWord = new ComparisonUnitWord(g.Select(gc => gc.ComparisonUnitAtomMember)),
+                            HierarchicalGroupingArray = hierarchicalGroupingArray
+                        };
+                    }
+                )
+                .ToArray();
+
+            if (False)
+            {
+                var sb = new StringBuilder();
+                foreach (WithHierarchicalGroupingKey group in withHierarchicalGroupingKey)
+                {
+                    sb.Append("Grouping Array: " +
+                              @group.HierarchicalGroupingArray.Select(gam => gam + " - ").StringConcatenate() +
+                              Environment.NewLine);
+                    foreach (ComparisonUnit gc in @group.ComparisonUnitWord.Contents)
+                    {
+                        sb.Append("    " + gc.ToString(0) + Environment.NewLine);
+                    }
+                }
+
+                string sbs = sb.ToString();
+                TestUtil.NotePad(sbs);
+            }
+
+            ComparisonUnit[] cul = GetHierarchicalComparisonUnits(withHierarchicalGroupingKey, 0).ToArray();
+
+            if (False)
+            {
+                string str = ComparisonUnit.ComparisonUnitListToString(cul);
+                TestUtil.NotePad(str);
+            }
+
+            return cul;
+        }
+
+        private static IEnumerable<ComparisonUnit> GetHierarchicalComparisonUnits(
+            IEnumerable<WithHierarchicalGroupingKey> input,
+            int level)
+        {
+            IEnumerable<IGrouping<string, WithHierarchicalGroupingKey>> grouped = input
+                .GroupAdjacent(
+                    whgk => level >= whgk.HierarchicalGroupingArray.Length ? "" : whgk.HierarchicalGroupingArray[level]);
+
+            List<ComparisonUnit> retList = grouped
+                .Select(gc =>
+                {
+                    if (gc.Key == "")
+                    {
+                        return (IEnumerable<ComparisonUnit>) gc.Select(whgk => whgk.ComparisonUnitWord).ToList();
+                    }
+
+                    string[] spl = gc.Key.Split(':');
+                    ComparisonUnitGroupType groupType = WmlComparerUtil.ComparisonUnitGroupTypeFromLocalName(spl[0]);
+                    IEnumerable<ComparisonUnit> childHierarchicalComparisonUnits = GetHierarchicalComparisonUnits(gc, level + 1);
+                    var newCompUnitGroup = new ComparisonUnitGroup(childHierarchicalComparisonUnits, groupType, level);
+
+                    return new[] { newCompUnitGroup };
+                })
+                .SelectMany(m => m)
+                .ToList();
+
+            return retList;
+        }
+
+        #endregion GetComparisonUnitList
+    }
+}

+ 276 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Fields.cs

@@ -0,0 +1,276 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+#pragma warning disable 414
+        private static readonly bool False = false;
+        private static readonly bool True = true;
+        private static readonly bool SaveIntermediateFilesForDebugging = false;
+#pragma warning restore 414
+
+        private static readonly string NewLine = Environment.NewLine;
+
+        private static readonly XAttribute[] NamespaceAttributes =
+        {
+            new XAttribute(XNamespace.Xmlns + "wpc", WPC.wpc),
+            new XAttribute(XNamespace.Xmlns + "mc", MC.mc),
+            new XAttribute(XNamespace.Xmlns + "o", O.o),
+            new XAttribute(XNamespace.Xmlns + "r", R.r),
+            new XAttribute(XNamespace.Xmlns + "m", M.m),
+            new XAttribute(XNamespace.Xmlns + "v", VML.vml),
+            new XAttribute(XNamespace.Xmlns + "wp14", WP14.wp14),
+            new XAttribute(XNamespace.Xmlns + "wp", WP.wp),
+            new XAttribute(XNamespace.Xmlns + "w10", W10.w10),
+            new XAttribute(XNamespace.Xmlns + "w", W.w),
+            new XAttribute(XNamespace.Xmlns + "w14", W14.w14),
+            new XAttribute(XNamespace.Xmlns + "wpg", WPG.wpg),
+            new XAttribute(XNamespace.Xmlns + "wpi", WPI.wpi),
+            new XAttribute(XNamespace.Xmlns + "wne", WNE.wne),
+            new XAttribute(XNamespace.Xmlns + "wps", WPS.wps),
+            new XAttribute(MC.Ignorable, "w14 wp14")
+        };
+
+        private static readonly XName[] RevElementsWithNoText =
+        {
+            M.oMath,
+            M.oMathPara,
+            W.drawing
+        };
+
+        private static readonly XName[] AttributesToTrimWhenCloning =
+        {
+            WP14.anchorId,
+            WP14.editId,
+            "ObjectID",
+            "ShapeID",
+            "id",
+            "type"
+        };
+
+        private static int _maxId;
+
+        private static readonly XName[] WordBreakElements =
+        {
+            W.pPr,
+            W.tab,
+            W.br,
+            W.continuationSeparator,
+            W.cr,
+            W.dayLong,
+            W.dayShort,
+            W.drawing,
+            W.pict,
+            W.endnoteRef,
+            W.footnoteRef,
+            W.monthLong,
+            W.monthShort,
+            W.noBreakHyphen,
+            W._object,
+            W.ptab,
+            W.separator,
+            W.sym,
+            W.yearLong,
+            W.yearShort,
+            M.oMathPara,
+            M.oMath,
+            W.footnoteReference,
+            W.endnoteReference
+        };
+
+        private static readonly XName[] AllowableRunChildren =
+        {
+            W.br,
+            W.drawing,
+            W.cr,
+            W.dayLong,
+            W.dayShort,
+            W.footnoteReference,
+            W.endnoteReference,
+            W.monthLong,
+            W.monthShort,
+            W.noBreakHyphen,
+
+            //W._object,
+            W.pgNum,
+            W.ptab,
+            W.softHyphen,
+            W.sym,
+            W.tab,
+            W.yearLong,
+            W.yearShort,
+            M.oMathPara,
+            M.oMath,
+            W.fldChar,
+            W.instrText
+        };
+
+        private static readonly XName[] ElementsToThrowAway =
+        {
+            W.bookmarkStart,
+            W.bookmarkEnd,
+            W.commentRangeStart,
+            W.commentRangeEnd,
+            W.lastRenderedPageBreak,
+            W.proofErr,
+            W.tblPr,
+            W.sectPr,
+            W.permEnd,
+            W.permStart,
+            W.footnoteRef,
+            W.endnoteRef,
+            W.separator,
+            W.continuationSeparator
+        };
+
+        private static readonly XName[] ElementsToHaveSha1Hash =
+        {
+            W.p,
+            W.tbl,
+            W.tr,
+            W.tc,
+            W.drawing,
+            W.pict,
+            W.txbxContent
+        };
+
+        private static readonly XName[] InvalidElements =
+        {
+            W.altChunk,
+            W.customXml,
+            W.customXmlDelRangeEnd,
+            W.customXmlDelRangeStart,
+            W.customXmlInsRangeEnd,
+            W.customXmlInsRangeStart,
+            W.customXmlMoveFromRangeEnd,
+            W.customXmlMoveFromRangeStart,
+            W.customXmlMoveToRangeEnd,
+            W.customXmlMoveToRangeStart,
+            W.moveFrom,
+            W.moveFromRangeStart,
+            W.moveFromRangeEnd,
+            W.moveTo,
+            W.moveToRangeStart,
+            W.moveToRangeEnd,
+            W.subDoc
+        };
+
+        private static readonly RecursionInfo[] RecursionElements =
+        {
+            new RecursionInfo
+            {
+                ElementName = W.del,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.ins,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.tbl,
+                ChildElementPropertyNames = new[] { W.tblPr, W.tblGrid, W.tblPrEx }
+            },
+            new RecursionInfo
+            {
+                ElementName = W.tr,
+                ChildElementPropertyNames = new[] { W.trPr, W.tblPrEx }
+            },
+            new RecursionInfo
+            {
+                ElementName = W.tc,
+                ChildElementPropertyNames = new[] { W.tcPr, W.tblPrEx }
+            },
+            new RecursionInfo
+            {
+                ElementName = W.pict,
+                ChildElementPropertyNames = new[] { VML.shapetype }
+            },
+            new RecursionInfo
+            {
+                ElementName = VML.group,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = VML.shape,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = VML.rect,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = VML.textbox,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = O._lock,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.txbxContent,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W10.wrap,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.sdt,
+                ChildElementPropertyNames = new[] { W.sdtPr, W.sdtEndPr }
+            },
+            new RecursionInfo
+            {
+                ElementName = W.sdtContent,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.hyperlink,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.fldSimple,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = VML.shapetype,
+                ChildElementPropertyNames = null
+            },
+            new RecursionInfo
+            {
+                ElementName = W.smartTag,
+                ChildElementPropertyNames = new[] { W.smartTagPr }
+            },
+            new RecursionInfo
+            {
+                ElementName = W.ruby,
+                ChildElementPropertyNames = new[] { W.rubyPr }
+            }
+        };
+
+        private static readonly XName[] ComparisonGroupingElements =
+        {
+            W.p,
+            W.tbl,
+            W.tr,
+            W.tc,
+            W.txbxContent
+        };
+    }
+}

+ 328 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Hashing.cs

@@ -0,0 +1,328 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        private static WmlDocument HashBlockLevelContent(
+            WmlDocument source,
+            WmlDocument sourceAfterProc,
+            WmlComparerSettings settings)
+        {
+            using (var msSource = new MemoryStream())
+            using (var msAfterProc = new MemoryStream())
+            {
+                msSource.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length);
+                msAfterProc.Write(sourceAfterProc.DocumentByteArray, 0, sourceAfterProc.DocumentByteArray.Length);
+
+                using (WordprocessingDocument wDocSource = WordprocessingDocument.Open(msSource, true))
+                using (WordprocessingDocument wDocAfterProc = WordprocessingDocument.Open(msAfterProc, true))
+                {
+                    // create Unid dictionary for source
+                    XDocument sourceMainXDoc = wDocSource.MainDocumentPart.GetXDocument();
+                    XElement sourceMainRoot = sourceMainXDoc.Root ?? throw new ArgumentException();
+                    Dictionary<string, XElement> sourceUnidDict = sourceMainRoot
+                        .Descendants()
+                        .Where(d => d.Name == W.p || d.Name == W.tbl || d.Name == W.tr)
+                        .ToDictionary(d => (string) d.Attribute(PtOpenXml.Unid));
+
+                    XDocument afterProcMainXDoc = wDocAfterProc.MainDocumentPart.GetXDocument();
+                    XElement afterProcMainRoot = afterProcMainXDoc.Root ?? throw new ArgumentException();
+                    IEnumerable<XElement> blockLevelElements = afterProcMainRoot
+                        .Descendants()
+                        .Where(d => d.Name == W.p || d.Name == W.tbl || d.Name == W.tr);
+
+                    foreach (XElement blockLevelContent in blockLevelElements)
+                    {
+                        var cloneBlockLevelContentForHashing = (XElement) CloneBlockLevelContentForHashing(
+                            wDocAfterProc.MainDocumentPart,
+                            blockLevelContent,
+                            true,
+                            settings);
+
+                        string shaString = cloneBlockLevelContentForHashing
+                            .ToString(SaveOptions.DisableFormatting)
+                            .Replace(" xmlns=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"", "");
+
+                        string sha1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(shaString);
+                        var thisUnid = (string) blockLevelContent.Attribute(PtOpenXml.Unid);
+                        if (thisUnid != null)
+                        {
+                            if (sourceUnidDict.ContainsKey(thisUnid))
+                            {
+                                XElement correlatedBlockLevelContent = sourceUnidDict[thisUnid];
+                                correlatedBlockLevelContent.Add(new XAttribute(PtOpenXml.CorrelatedSHA1Hash, sha1Hash));
+                            }
+                        }
+                    }
+
+                    wDocSource.MainDocumentPart.PutXDocument();
+                }
+
+                var sourceWithCorrelatedSHA1Hash = new WmlDocument(source.FileName, msSource.ToArray());
+                return sourceWithCorrelatedSHA1Hash;
+            }
+        }
+
+        // prohibit
+        // - altChunk
+        // - subDoc
+        // - contentPart
+
+        // This strips all text nodes from the XML tree, thereby leaving only the structure.
+
+        private static object CloneBlockLevelContentForHashing(
+            OpenXmlPart mainDocumentPart,
+            XNode node,
+            bool includeRelatedParts,
+            WmlComparerSettings settings)
+        {
+            if (node is XElement element)
+            {
+                if (element.Name == W.bookmarkStart ||
+                    element.Name == W.bookmarkEnd ||
+                    element.Name == W.pPr ||
+                    element.Name == W.rPr)
+                {
+                    return null;
+                }
+
+                if (element.Name == W.p)
+                {
+                    var clonedPara = new XElement(element.Name,
+                        element.Attributes().Where(a => a.Name != W.rsid &&
+                                                        a.Name != W.rsidDel &&
+                                                        a.Name != W.rsidP &&
+                                                        a.Name != W.rsidR &&
+                                                        a.Name != W.rsidRDefault &&
+                                                        a.Name != W.rsidRPr &&
+                                                        a.Name != W.rsidSect &&
+                                                        a.Name != W.rsidTr &&
+                                                        a.Name.Namespace != PtOpenXml.pt),
+                        element.Nodes().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+
+                    IEnumerable<IGrouping<bool, XElement>> groupedRuns = clonedPara
+                        .Elements()
+                        .GroupAdjacent(e => e.Name == W.r &&
+                                            e.Elements().Count() == 1 &&
+                                            e.Element(W.t) != null);
+
+                    var clonedParaWithGroupedRuns = new XElement(element.Name,
+                        groupedRuns.Select(g =>
+                        {
+                            if (g.Key)
+                            {
+                                string text = g.Select(t => t.Value).StringConcatenate();
+                                if (settings.CaseInsensitive)
+                                    text = text.ToUpper(settings.CultureInfo);
+                                var newRun = (object) new XElement(W.r,
+                                    new XElement(W.t,
+                                        text));
+                                return newRun;
+                            }
+
+                            return g;
+                        }));
+
+                    return clonedParaWithGroupedRuns;
+                }
+
+                if (element.Name == W.r)
+                {
+                    IEnumerable<XElement> clonedRuns = element
+                        .Elements()
+                        .Where(e => e.Name != W.rPr)
+                        .Select(rc => new XElement(W.r,
+                            CloneBlockLevelContentForHashing(mainDocumentPart, rc, includeRelatedParts, settings)));
+                    return clonedRuns;
+                }
+
+                if (element.Name == W.tbl)
+                {
+                    var clonedTable = new XElement(W.tbl,
+                        element.Elements(W.tr).Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return clonedTable;
+                }
+
+                if (element.Name == W.tr)
+                {
+                    var clonedRow = new XElement(W.tr,
+                        element.Elements(W.tc).Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return clonedRow;
+                }
+
+                if (element.Name == W.tc)
+                {
+                    var clonedCell = new XElement(W.tc,
+                        element.Elements().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return clonedCell;
+                }
+
+                if (element.Name == W.tcPr)
+                {
+                    var clonedCellProps = new XElement(W.tcPr,
+                        element.Elements(W.gridSpan).Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return clonedCellProps;
+                }
+
+                if (element.Name == W.gridSpan)
+                {
+                    var clonedGridSpan = new XElement(W.gridSpan,
+                        new XAttribute("val", (string) element.Attribute(W.val)));
+                    return clonedGridSpan;
+                }
+
+                if (element.Name == W.txbxContent)
+                {
+                    var clonedTextbox = new XElement(W.txbxContent,
+                        element.Elements().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return clonedTextbox;
+                }
+
+                if (includeRelatedParts)
+                {
+                    if (ComparisonUnitWord.ElementsWithRelationshipIds.Contains(element.Name))
+                    {
+                        var newElement = new XElement(element.Name,
+                            element.Attributes()
+                                .Where(a => a.Name.Namespace != PtOpenXml.pt)
+                                .Where(a => !AttributesToTrimWhenCloning.Contains(a.Name))
+                                .Select(a =>
+                                {
+                                    if (!ComparisonUnitWord.RelationshipAttributeNames.Contains(a.Name))
+                                        return a;
+
+                                    var rId = (string) a;
+
+                                    // could be an hyperlink relationship
+                                    try
+                                    {
+                                        OpenXmlPart oxp = mainDocumentPart.GetPartById(rId);
+                                        if (oxp == null)
+                                            throw new FileFormatException("Invalid WordprocessingML Document");
+
+                                        var anno = oxp.Annotation<PartSHA1HashAnnotation>();
+                                        if (anno != null)
+                                            return new XAttribute(a.Name, anno.Hash);
+
+                                        if (!oxp.ContentType.EndsWith("xml"))
+                                        {
+                                            using (Stream str = oxp.GetStream())
+                                            {
+                                                byte[] ba;
+                                                using (var br = new BinaryReader(str))
+                                                {
+                                                    ba = br.ReadBytes((int) str.Length);
+                                                }
+
+                                                string sha1 = WmlComparerUtil.SHA1HashStringForByteArray(ba);
+                                                oxp.AddAnnotation(new PartSHA1HashAnnotation(sha1));
+                                                return new XAttribute(a.Name, sha1);
+                                            }
+                                        }
+                                    }
+                                    catch (ArgumentOutOfRangeException)
+                                    {
+                                        HyperlinkRelationship hr =
+                                            mainDocumentPart.HyperlinkRelationships.FirstOrDefault(z => z.Id == rId);
+                                        if (hr != null)
+                                        {
+                                            string str = hr.Uri.ToString();
+                                            return new XAttribute(a.Name, str);
+                                        }
+
+                                        // could be an external relationship
+                                        ExternalRelationship er =
+                                            mainDocumentPart.ExternalRelationships.FirstOrDefault(z => z.Id == rId);
+                                        if (er != null)
+                                        {
+                                            string str = er.Uri.ToString();
+                                            return new XAttribute(a.Name, str);
+                                        }
+
+                                        return new XAttribute(a.Name, "NULL Relationship");
+                                    }
+
+                                    return null;
+                                }),
+                            element.Nodes().Select(n =>
+                                CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                        return newElement;
+                    }
+                }
+
+                if (element.Name == VML.shape)
+                {
+                    return new XElement(element.Name,
+                        element.Attributes()
+                            .Where(a => a.Name.Namespace != PtOpenXml.pt)
+                            .Where(a => a.Name != "style" && a.Name != "id" && a.Name != "type"),
+                        element.Nodes().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                }
+
+                if (element.Name == O.OLEObject)
+                {
+                    var o = new XElement(element.Name,
+                        element.Attributes()
+                            .Where(a => a.Name.Namespace != PtOpenXml.pt)
+                            .Where(a => a.Name != "ObjectID" && a.Name != R.id),
+                        element.Nodes().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return o;
+                }
+
+                if (element.Name == W._object)
+                {
+                    var o = new XElement(element.Name,
+                        element.Attributes()
+                            .Where(a => a.Name.Namespace != PtOpenXml.pt),
+                        element.Nodes().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                    return o;
+                }
+
+                if (element.Name == WP.docPr)
+                {
+                    return new XElement(element.Name,
+                        element.Attributes()
+                            .Where(a => a.Name.Namespace != PtOpenXml.pt && a.Name != "id"),
+                        element.Nodes().Select(n =>
+                            CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+                }
+
+                return new XElement(element.Name,
+                    element.Attributes()
+                        .Where(a => a.Name.Namespace != PtOpenXml.pt)
+                        .Where(a => !AttributesToTrimWhenCloning.Contains(a.Name)),
+                    element.Nodes().Select(n =>
+                        CloneBlockLevelContentForHashing(mainDocumentPart, n, includeRelatedParts, settings)));
+            }
+
+            if (settings.CaseInsensitive)
+            {
+                if (node is XText xt)
+                {
+                    string newText = xt.Value.ToUpper(settings.CultureInfo);
+                    return new XText(newText);
+                }
+            }
+
+            return node;
+        }
+    }
+}

File diff suppressed because it is too large
+ 1326 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Lcs.cs


+ 331 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.PreProcessMarkup.cs

@@ -0,0 +1,331 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        private static WmlDocument PreProcessMarkup(WmlDocument source, int startingIdForFootnotesEndnotes)
+        {
+            // open and close to get rid of MC content
+            using (var ms = new MemoryStream())
+            {
+                ms.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length);
+                var os = new OpenSettings
+                {
+                    MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(
+                        MarkupCompatibilityProcessMode.ProcessAllParts,
+                        FileFormatVersions.Office2007)
+                };
+
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true, os))
+                {
+                    OpenXmlPartRootElement unused = wDoc.MainDocumentPart.RootElement;
+                    if (wDoc.MainDocumentPart.FootnotesPart != null)
+                    {
+                        // contrary to what you might think, looking at the API, it is necessary to access the root element of each part to cause
+                        // the SDK to process MC markup.
+                        OpenXmlPartRootElement unused1 = wDoc.MainDocumentPart.FootnotesPart.RootElement;
+                    }
+
+                    if (wDoc.MainDocumentPart.EndnotesPart != null)
+                    {
+                        OpenXmlPartRootElement unused1 = wDoc.MainDocumentPart.EndnotesPart.RootElement;
+                    }
+                }
+
+                source = new WmlDocument(source.FileName, ms.ToArray());
+            }
+
+            // open and close to get rid of MC content
+            using (var ms = new MemoryStream())
+            {
+                ms.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length);
+                var os = new OpenSettings
+                {
+                    MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(
+                        MarkupCompatibilityProcessMode.ProcessAllParts,
+                        FileFormatVersions.Office2007)
+                };
+
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true, os))
+                {
+                    TestForInvalidContent(wDoc);
+                    RemoveExistingPowerToolsMarkup(wDoc);
+
+                    // Removing content controls, field codes, and bookmarks is a no-no for many use cases.
+                    // We need content controls, e.g., on the title page. Field codes are required for
+                    // automatic cross-references, which require bookmarks.
+                    // TODO: Revisit
+                    var msSettings = new SimplifyMarkupSettings
+                    {
+                        RemoveBookmarks = true,
+
+                        AcceptRevisions = false,
+                        RemoveComments = true,
+
+                        RemoveContentControls = true,
+                        RemoveFieldCodes = true,
+
+                        RemoveGoBackBookmark = true,
+                        RemoveLastRenderedPageBreak = true,
+                        RemovePermissions = true,
+                        RemoveProof = true,
+                        RemoveSmartTags = true,
+                        RemoveSoftHyphens = true,
+                        RemoveHyperlinks = true
+                    };
+                    MarkupSimplifier.SimplifyMarkup(wDoc, msSettings);
+                    ChangeFootnoteEndnoteReferencesToUniqueRange(wDoc, startingIdForFootnotesEndnotes);
+                    AddUnidsToMarkupInContentParts(wDoc);
+                    AddFootnotesEndnotesParts(wDoc);
+                    FillInEmptyFootnotesEndnotes(wDoc);
+                }
+
+                return new WmlDocument(source.FileName, ms.ToArray());
+            }
+        }
+
+        private static void TestForInvalidContent(WordprocessingDocument wDoc)
+        {
+            foreach (OpenXmlPart part in wDoc.ContentParts())
+            {
+                XDocument xDoc = part.GetXDocument();
+                if (xDoc.Descendants(W.altChunk).Any())
+                    throw new OpenXmlPowerToolsException("Unsupported document, contains w:altChunk");
+                if (xDoc.Descendants(W.subDoc).Any())
+                    throw new OpenXmlPowerToolsException("Unsupported document, contains w:subDoc");
+                if (xDoc.Descendants(W.contentPart).Any())
+                    throw new OpenXmlPowerToolsException("Unsupported document, contains w:contentPart");
+            }
+        }
+
+        private static void RemoveExistingPowerToolsMarkup(WordprocessingDocument wDoc)
+        {
+            wDoc.MainDocumentPart
+                .GetXDocument()
+                .Root?
+                .Descendants()
+                .Attributes()
+                .Where(a => a.Name.Namespace == PtOpenXml.pt)
+                .Where(a => a.Name != PtOpenXml.Unid)
+                .Remove();
+
+            wDoc.MainDocumentPart.PutXDocument();
+
+            FootnotesPart fnPart = wDoc.MainDocumentPart.FootnotesPart;
+            if (fnPart != null)
+            {
+                XDocument fnXDoc = fnPart.GetXDocument();
+                fnXDoc
+                    .Root?
+                    .Descendants()
+                    .Attributes()
+                    .Where(a => a.Name.Namespace == PtOpenXml.pt)
+                    .Where(a => a.Name != PtOpenXml.Unid)
+                    .Remove();
+
+                fnPart.PutXDocument();
+            }
+
+            EndnotesPart enPart = wDoc.MainDocumentPart.EndnotesPart;
+            if (enPart != null)
+            {
+                XDocument enXDoc = enPart.GetXDocument();
+                enXDoc
+                    .Root?
+                    .Descendants()
+                    .Attributes()
+                    .Where(a => a.Name.Namespace == PtOpenXml.pt)
+                    .Where(a => a.Name != PtOpenXml.Unid)
+                    .Remove();
+
+                enPart.PutXDocument();
+            }
+        }
+
+        private static void ChangeFootnoteEndnoteReferencesToUniqueRange(
+            WordprocessingDocument wDoc,
+            int startingIdForFootnotesEndnotes)
+        {
+            MainDocumentPart mainDocPart = wDoc.MainDocumentPart;
+            FootnotesPart footnotesPart = wDoc.MainDocumentPart.FootnotesPart;
+            EndnotesPart endnotesPart = wDoc.MainDocumentPart.EndnotesPart;
+
+            XElement document =
+                mainDocPart.GetXDocument().Root ?? throw new OpenXmlPowerToolsException("Invalid document.");
+
+            XElement footnotes = footnotesPart?.GetXDocument().Root;
+            XElement endnotes = endnotesPart?.GetXDocument().Root;
+
+            IEnumerable<XElement> references = document
+                .Descendants()
+                .Where(d => d.Name == W.footnoteReference || d.Name == W.endnoteReference);
+
+            foreach (XElement r in references)
+            {
+                var oldId = (string) r.Attribute(W.id);
+                string newId = startingIdForFootnotesEndnotes.ToString();
+                startingIdForFootnotesEndnotes++;
+                r.SetAttributeValue(W.id, newId);
+                if (r.Name == W.footnoteReference)
+                {
+                    XElement fn = footnotes?
+                        .Elements()
+                        .FirstOrDefault(e => (string) e.Attribute(W.id) == oldId);
+
+                    if (fn == null)
+                    {
+                        throw new OpenXmlPowerToolsException("Invalid document");
+                    }
+
+                    fn.SetAttributeValue(W.id, newId);
+                }
+                else
+                {
+                    XElement en = endnotes?
+                        .Elements()
+                        .FirstOrDefault(e => (string) e.Attribute(W.id) == oldId);
+
+                    if (en == null)
+                    {
+                        throw new OpenXmlPowerToolsException("Invalid document");
+                    }
+
+                    en.SetAttributeValue(W.id, newId);
+                }
+            }
+
+            mainDocPart.PutXDocument();
+            footnotesPart?.PutXDocument();
+            endnotesPart?.PutXDocument();
+        }
+
+        private static void AddUnidsToMarkupInContentParts(WordprocessingDocument wDoc)
+        {
+            XDocument mdp = wDoc.MainDocumentPart.GetXDocument();
+            AssignUnidToAllElements(mdp.Root);
+            IgnorePt14Namespace(mdp.Root);
+            wDoc.MainDocumentPart.PutXDocument();
+
+            if (wDoc.MainDocumentPart.FootnotesPart != null)
+            {
+                XDocument p = wDoc.MainDocumentPart.FootnotesPart.GetXDocument();
+                AssignUnidToAllElements(p.Root);
+                IgnorePt14Namespace(p.Root);
+                wDoc.MainDocumentPart.FootnotesPart.PutXDocument();
+            }
+
+            if (wDoc.MainDocumentPart.EndnotesPart != null)
+            {
+                XDocument p = wDoc.MainDocumentPart.EndnotesPart.GetXDocument();
+                AssignUnidToAllElements(p.Root);
+                IgnorePt14Namespace(p.Root);
+                wDoc.MainDocumentPart.EndnotesPart.PutXDocument();
+            }
+        }
+
+        private static void AssignUnidToAllElements(XElement contentParent)
+        {
+            IEnumerable<XElement> content = contentParent.Descendants();
+            foreach (XElement d in content)
+            {
+                if (d.Attribute(PtOpenXml.Unid) == null)
+                {
+                    string unid = Guid.NewGuid().ToString().Replace("-", "");
+                    var newAtt = new XAttribute(PtOpenXml.Unid, unid);
+                    d.Add(newAtt);
+                }
+            }
+        }
+
+        [SuppressMessage("ReSharper", "CoVariantArrayConversion")]
+        private static void AddFootnotesEndnotesParts(WordprocessingDocument wDoc)
+        {
+            MainDocumentPart mdp = wDoc.MainDocumentPart;
+            if (mdp.FootnotesPart == null)
+            {
+                mdp.AddNewPart<FootnotesPart>();
+                XDocument newFootnotes = wDoc.MainDocumentPart.FootnotesPart.GetXDocument();
+                newFootnotes.Declaration.Standalone = "yes";
+                newFootnotes.Declaration.Encoding = "UTF-8";
+                newFootnotes.Add(new XElement(W.footnotes, NamespaceAttributes));
+                mdp.FootnotesPart.PutXDocument();
+            }
+
+            if (mdp.EndnotesPart == null)
+            {
+                mdp.AddNewPart<EndnotesPart>();
+                XDocument newEndnotes = wDoc.MainDocumentPart.EndnotesPart.GetXDocument();
+                newEndnotes.Declaration.Standalone = "yes";
+                newEndnotes.Declaration.Encoding = "UTF-8";
+                newEndnotes.Add(new XElement(W.endnotes, NamespaceAttributes));
+                mdp.EndnotesPart.PutXDocument();
+            }
+        }
+
+        private static void FillInEmptyFootnotesEndnotes(WordprocessingDocument wDoc)
+        {
+            XElement emptyFootnote = XElement.Parse(
+                @"<w:p xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
+  <w:pPr>
+    <w:pStyle w:val='FootnoteText'/>
+  </w:pPr>
+  <w:r>
+    <w:rPr>
+      <w:rStyle w:val='FootnoteReference'/>
+    </w:rPr>
+    <w:footnoteRef/>
+  </w:r>
+</w:p>");
+
+            XElement emptyEndnote = XElement.Parse(
+                @"<w:p xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'>
+  <w:pPr>
+    <w:pStyle w:val='EndnoteText'/>
+  </w:pPr>
+  <w:r>
+    <w:rPr>
+      <w:rStyle w:val='EndnoteReference'/>
+    </w:rPr>
+    <w:endnoteRef/>
+  </w:r>
+</w:p>");
+
+            FootnotesPart footnotePart = wDoc.MainDocumentPart.FootnotesPart;
+            if (footnotePart != null)
+            {
+                XElement fnRoot = footnotePart.GetXDocument().Root ?? throw new ArgumentException();
+                foreach (XElement fn in fnRoot.Elements(W.footnote))
+                {
+                    if (!fn.HasElements)
+                        fn.Add(emptyFootnote);
+                }
+
+                footnotePart.PutXDocument();
+            }
+
+            EndnotesPart endnotePart = wDoc.MainDocumentPart.EndnotesPart;
+            if (endnotePart != null)
+            {
+                XElement fnRoot = endnotePart.GetXDocument().Root ?? throw new ArgumentException();
+                foreach (XElement fn in fnRoot.Elements(W.endnote))
+                {
+                    if (!fn.HasElements)
+                        fn.Add(emptyEndnote);
+                }
+
+                endnotePart.PutXDocument();
+            }
+        }
+    }
+}

File diff suppressed because it is too large
+ 3232 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.ProduceDocument.cs


+ 103 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.Methods.Util.cs

@@ -0,0 +1,103 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Packaging;
+using System.Linq;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        private static XElement MoveRelatedPartsToDestination(
+            PackagePart partOfDeletedContent,
+            PackagePart partInNewDocument,
+            XElement contentElement)
+        {
+            List<XElement> elementsToUpdate = contentElement
+                .Descendants()
+                .Where(d => d.Attributes().Any(a => ComparisonUnitWord.RelationshipAttributeNames.Contains(a.Name)))
+                .ToList();
+
+            foreach (XElement element in elementsToUpdate)
+            {
+                List<XAttribute> attributesToUpdate = element
+                    .Attributes()
+                    .Where(a => ComparisonUnitWord.RelationshipAttributeNames.Contains(a.Name))
+                    .ToList();
+
+                foreach (XAttribute att in attributesToUpdate)
+                {
+                    var rId = (string) att;
+
+                    PackageRelationship relationshipForDeletedPart = partOfDeletedContent.GetRelationship(rId);
+
+                    Uri targetUri = PackUriHelper
+                        .ResolvePartUri(
+                            new Uri(partOfDeletedContent.Uri.ToString(), UriKind.Relative),
+                            relationshipForDeletedPart.TargetUri);
+
+                    PackagePart relatedPackagePart = partOfDeletedContent.Package.GetPart(targetUri);
+                    string[] uriSplit = relatedPackagePart.Uri.ToString().Split('/');
+                    string[] last = uriSplit[uriSplit.Length - 1].Split('.');
+                    string uriString;
+                    if (last.Length == 2)
+                    {
+                        uriString = uriSplit.SkipLast(1).Select(p => p + "/").StringConcatenate() +
+                                    "P" + Guid.NewGuid().ToString().Replace("-", "") + "." + last[1];
+                    }
+                    else
+                    {
+                        uriString = uriSplit.SkipLast(1).Select(p => p + "/").StringConcatenate() +
+                                    "P" + Guid.NewGuid().ToString().Replace("-", "");
+                    }
+
+                    Uri uri = relatedPackagePart.Uri.IsAbsoluteUri
+                        ? new Uri(uriString, UriKind.Absolute)
+                        : new Uri(uriString, UriKind.Relative);
+
+                    PackagePart newPart = partInNewDocument.Package.CreatePart(uri, relatedPackagePart.ContentType);
+
+                    // ReSharper disable once PossibleNullReferenceException
+                    using (Stream oldPartStream = relatedPackagePart.GetStream())
+                    using (Stream newPartStream = newPart.GetStream())
+                    {
+                        FileUtils.CopyStream(oldPartStream, newPartStream);
+                    }
+
+                    string newRid = "R" + Guid.NewGuid().ToString().Replace("-", "");
+                    partInNewDocument.CreateRelationship(newPart.Uri, TargetMode.Internal,
+                        relationshipForDeletedPart.RelationshipType, newRid);
+                    att.Value = newRid;
+
+                    if (newPart.ContentType.EndsWith("xml"))
+                    {
+                        XDocument newPartXDoc;
+                        using (Stream stream = newPart.GetStream())
+                        {
+                            newPartXDoc = XDocument.Load(stream);
+                            MoveRelatedPartsToDestination(relatedPackagePart, newPart, newPartXDoc.Root);
+                        }
+
+                        using (Stream stream = newPart.GetStream())
+                            newPartXDoc.Save(stream);
+                    }
+                }
+            }
+
+            return contentElement;
+        }
+
+        private static XAttribute GetXmlSpaceAttribute(string textOfTextElement)
+        {
+            if (char.IsWhiteSpace(textOfTextElement[0]) ||
+                char.IsWhiteSpace(textOfTextElement[textOfTextElement.Length - 1]))
+                return new XAttribute(XNamespace.Xml + "space", "preserve");
+
+            return null;
+        }
+    }
+}

+ 36 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Private.NestedTypes.cs

@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        private class Atgbw
+        {
+            public int? Key;
+            public ComparisonUnitAtom ComparisonUnitAtomMember;
+            public int NextIndex;
+        }
+
+        private class ConsolidationInfo
+        {
+            public string Revisor;
+            public Color Color;
+            public XElement RevisionElement;
+            public bool InsertBefore;
+            public string RevisionHash;
+            public XElement[] Footnotes;
+            public XElement[] Endnotes;
+            public string RevisionString; // for debugging purposes only
+        }
+
+        private class RecursionInfo
+        {
+            public XName ElementName;
+            public XName[] ChildElementPropertyNames;
+        }
+    }
+}

+ 227 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.Compare.cs

@@ -0,0 +1,227 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+// It is possible to optimize DescendantContentAtoms
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Currently, the unid is set at the beginning of the algorithm.  It is used by the code that establishes correlation
+// based on first rejecting// tracked revisions, then correlating paragraphs/tables.  It is requred for this algorithm
+// - after finding a correlated sequence in the document with rejected revisions, it uses the unid to find the same
+// paragraph in the document without rejected revisions, then sets the correlated sha1 hash in that document.
+//
+// But then when accepting tracked revisions, for certain paragraphs (where there are deleted paragraph marks) it is
+// going to lose the unids.  But this isn't a problem because when paragraph marks are deleted, the correlation is
+// definitely no longer possible.  Any paragraphs that are in a range of paragraphs that are coalesced can't be
+// correlated to paragraphs in the other document via their hash.  At that point we no longer care what their unids
+// are.
+//
+// But after that it is only used to reconstruct the tree.  It is also used in the debugging code that
+// prints the various correlated sequences and comparison units - this is display for debugging purposes only.
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The key idea here is that a given paragraph will always have the same ancestors, and it doesn't matter whether the
+// content was deleted from the old document, inserted into the new document, or set as equal.  At this point, we
+// identify a paragraph as a sequential list of content atoms, terminated by a paragraph mark.  This entire list will
+// for a single paragraph, regardless of whether the paragraph is a child of the body, or if the paragraph is in a cell
+// in a table, or if the paragraph is in a text box.  The list of ancestors, from the paragraph to the root of the XML
+// tree will be the same for all content atoms in the paragraph.
+//
+// Therefore:
+//
+// Iterate through the list of content atoms backwards.  When the loop sees a paragraph mark, it gets the ancestor
+// unids from the paragraph mark to the top of the tree, and sets this as the same for all content atoms in the
+// paragraph.  For descendants of the paragraph mark, it doesn't really matter if content is put into separate runs
+// or what not.  We don't need to be concerned about what the unids are for descendants of the paragraph.
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        public static WmlDocument Compare(WmlDocument source1, WmlDocument source2, WmlComparerSettings settings)
+        {
+            return CompareInternal(source1, source2, settings, true);
+        }
+
+        private static WmlDocument CompareInternal(
+            WmlDocument source1,
+            WmlDocument source2,
+            WmlComparerSettings settings,
+            bool preProcessMarkupInOriginal)
+        {
+            if (preProcessMarkupInOriginal)
+            {
+                source1 = PreProcessMarkup(source1, settings.StartingIdForFootnotesEndnotes + 1000);
+            }
+
+            source2 = PreProcessMarkup(source2, settings.StartingIdForFootnotesEndnotes + 2000);
+
+            SaveDocumentIfDesired(source1, "Source1-Step1-PreProcess.docx", settings);
+            SaveDocumentIfDesired(source2, "Source2-Step1-PreProcess.docx", settings);
+
+            // at this point, both source1 and source2 have unid on every element.  These are the values that will
+            // enable reassembly of the XML tree.  But we need other values.
+
+            // In source1:
+            // - accept tracked revisions
+            // - determine hash code for every block-level element
+            // - save as attribute on every element
+
+            // - accept tracked revisions and reject tracked revisions leave the unids alone, where possible.
+            // - after accepting and calculating the hash, then can use the unids to find the right block-level
+            //   element in the unmodified source1, and install the hash
+
+            // In source2:
+            // - reject tracked revisions
+            // - determine hash code for every block-level element
+            // - save as an attribute on every element
+
+            // - after rejecting and calculating the hash, then can use the unids to find the right block-level element
+            //   in the unmodified source2, and install the hash
+
+            // - sometimes after accepting or rejecting tracked revisions, several paragraphs will get coalesced into a
+            //   single paragraph due to paragraph marks being inserted / deleted.
+            // - in this case, some paragraphs will not get a hash injected onto them.
+            // - if a paragraph doesn't have a hash, then it will never correspond to another paragraph, and such
+            //   issues will need to be resolved in the normal execution of the LCS algorithm.
+            // - note that when we do propagate the unid through for the first paragraph.
+
+            // Establish correlation between the two.
+            // Find the longest common sequence of block-level elements where hash codes are the same.
+            // this sometimes will be every block level element in the document.  Or sometimes will be just a fair
+            // number of them.
+
+            // at the start of doing the LCS algorithm, we will match up content, and put them in corresponding unknown
+            // correlated comparison units.  Those paragraphs will only ever be matched to their corresponding paragraph.
+            // then the algorithm can proceed as usual.
+
+            // need to call ChangeFootnoteEndnoteReferencesToUniqueRange before creating the wmlResult document, so that
+            // the same GUID ids are used for footnote and endnote references in both the 'after' document, and in the
+            // result document.
+
+            WmlDocument source1AfterAccepting = RevisionProcessor.AcceptRevisions(source1);
+            WmlDocument source2AfterRejecting = RevisionProcessor.RejectRevisions(source2);
+
+            SaveDocumentIfDesired(source1AfterAccepting, "Source1-Step2-AfterAccepting.docx", settings);
+            SaveDocumentIfDesired(source2AfterRejecting, "Source2-Step2-AfterRejecting.docx", settings);
+
+            // this creates the correlated hash codes that enable us to match up ranges of paragraphs based on
+            // accepting in source1, rejecting in source2
+            source1 = HashBlockLevelContent(source1, source1AfterAccepting, settings);
+            source2 = HashBlockLevelContent(source2, source2AfterRejecting, settings);
+
+            SaveDocumentIfDesired(source1, "Source1-Step3-AfterHashing.docx", settings);
+            SaveDocumentIfDesired(source2, "Source2-Step3-AfterHashing.docx", settings);
+
+            // Accept revisions in before, and after
+            source1 = RevisionProcessor.AcceptRevisions(source1);
+            source2 = RevisionProcessor.AcceptRevisions(source2);
+
+            SaveDocumentIfDesired(source1, "Source1-Step4-AfterAccepting.docx", settings);
+            SaveDocumentIfDesired(source2, "Source2-Step4-AfterAccepting.docx", settings);
+
+            // after accepting revisions, some unids may have been removed by revision accepter, along with the
+            // correlatedSHA1Hash codes, this is as it should be.
+            // but need to go back in and add guids to paragraphs that have had them removed.
+
+            using (var ms = new MemoryStream())
+            {
+                ms.Write(source2.DocumentByteArray, 0, source2.DocumentByteArray.Length);
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true))
+                {
+                    AddUnidsToMarkupInContentParts(wDoc);
+                }
+            }
+
+            var wmlResult = new WmlDocument(source1);
+            using (var ms1 = new MemoryStream())
+            using (var ms2 = new MemoryStream())
+            {
+                ms1.Write(source1.DocumentByteArray, 0, source1.DocumentByteArray.Length);
+                ms2.Write(source2.DocumentByteArray, 0, source2.DocumentByteArray.Length);
+                WmlDocument producedDocument;
+
+                using (WordprocessingDocument wDoc1 = WordprocessingDocument.Open(ms1, true))
+                using (WordprocessingDocument wDoc2 = WordprocessingDocument.Open(ms2, true))
+                {
+                    producedDocument = ProduceDocumentWithTrackedRevisions(settings, wmlResult, wDoc1, wDoc2);
+                }
+
+                SaveDocumentsAfterProducingDocument(ms1, ms2, settings);
+                SaveCleanedDocuments(source1, producedDocument, settings);
+
+                return producedDocument;
+            }
+        }
+
+        private static void SaveDocumentIfDesired(WmlDocument source, string name, WmlComparerSettings settings)
+        {
+            if (SaveIntermediateFilesForDebugging && settings.DebugTempFileDi != null)
+            {
+                var fileInfo = new FileInfo(Path.Combine(settings.DebugTempFileDi.FullName, name));
+                source.SaveAs(fileInfo.FullName);
+            }
+        }
+
+        private static void SaveDocumentsAfterProducingDocument(MemoryStream ms1, MemoryStream ms2, WmlComparerSettings settings)
+        {
+            if (SaveIntermediateFilesForDebugging && settings.DebugTempFileDi != null)
+            {
+                SaveDocumentIfDesired(new WmlDocument("after1.docx", ms1), "Source1-Step5-AfterProducingDocument.docx", settings);
+                SaveDocumentIfDesired(new WmlDocument("after2.docx", ms2), "Source2-Step5-AfterProducingDocument.docx", settings);
+            }
+        }
+
+        private static void SaveCleanedDocuments(WmlDocument source1, WmlDocument producedDocument, WmlComparerSettings settings)
+        {
+            if (SaveIntermediateFilesForDebugging && settings.DebugTempFileDi != null)
+            {
+                WmlDocument cleanedSource = CleanPowerToolsAndRsid(source1);
+                SaveDocumentIfDesired(cleanedSource, "Cleaned-Source.docx", settings);
+
+                WmlDocument cleanedProduced = CleanPowerToolsAndRsid(producedDocument);
+                SaveDocumentIfDesired(cleanedProduced, "Cleaned-Produced.docx", settings);
+            }
+        }
+
+        private static WmlDocument CleanPowerToolsAndRsid(WmlDocument producedDocument)
+        {
+            using (var ms = new MemoryStream())
+            {
+                ms.Write(producedDocument.DocumentByteArray, 0, producedDocument.DocumentByteArray.Length);
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true))
+                {
+                    foreach (OpenXmlPart cp in wDoc.ContentParts())
+                    {
+                        XDocument xd = cp.GetXDocument();
+                        object newRoot = CleanPartTransform(xd.Root);
+                        xd.Root?.ReplaceWith(newRoot);
+                        cp.PutXDocument();
+                    }
+                }
+
+                var cleaned = new WmlDocument("cleaned.docx", ms.ToArray());
+                return cleaned;
+            }
+        }
+
+        private static object CleanPartTransform(XNode node)
+        {
+            if (node is XElement element)
+            {
+                return new XElement(element.Name,
+                    element.Attributes().Where(a => a.Name.Namespace != PtOpenXml.pt &&
+                                                    !a.Name.LocalName.ToLower().Contains("rsid")),
+                    element.Nodes().Select(CleanPartTransform));
+            }
+
+            return node;
+        }
+    }
+}

File diff suppressed because it is too large
+ 1002 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.Consolidate.cs


+ 214 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.Methods.GetRevisions.cs

@@ -0,0 +1,214 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        // the following gets a flattened list of ComparisonUnitAtoms, with status indicated in each ComparisonUnitAtom: Deleted, Inserted, or Equal
+
+        // for any deleted or inserted rows, we go into the w:trPr properties, and add the appropriate w:ins or w:del element, and therefore
+        // when generating the document, the appropriate row will be marked as deleted or inserted.
+
+        public static List<WmlComparerRevision> GetRevisions(WmlDocument source, WmlComparerSettings settings)
+        {
+            using (var ms = new MemoryStream())
+            {
+                ms.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length);
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true))
+                {
+                    TestForInvalidContent(wDoc);
+                    RemoveExistingPowerToolsMarkup(wDoc);
+
+                    XElement contentParent = wDoc.MainDocumentPart.GetXDocument().Root?.Element(W.body);
+                    ComparisonUnitAtom[] atomList =
+                        CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings).ToArray();
+
+                    if (False)
+                    {
+                        var sb = new StringBuilder();
+                        foreach (ComparisonUnitAtom item in atomList)
+                            sb.Append(item + Environment.NewLine);
+                        string sbs = sb.ToString();
+                        TestUtil.NotePad(sbs);
+                    }
+
+                    List<IGrouping<string, ComparisonUnitAtom>> grouped = atomList
+                        .GroupAdjacent(a =>
+                        {
+                            string key = a.CorrelationStatus.ToString();
+                            if (a.CorrelationStatus != CorrelationStatus.Equal)
+                            {
+                                var rt = new XElement(a.RevTrackElement.Name,
+                                    new XAttribute(XNamespace.Xmlns + "w",
+                                        "http://schemas.openxmlformats.org/wordprocessingml/2006/main"),
+                                    a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid));
+                                key += rt.ToString(SaveOptions.DisableFormatting);
+                            }
+
+                            return key;
+                        })
+                        .ToList();
+
+                    List<IGrouping<string, ComparisonUnitAtom>> revisions = grouped
+                        .Where(k => k.Key != "Equal")
+                        .ToList();
+
+                    if (False)
+                    {
+                        var sb = new StringBuilder();
+                        foreach (IGrouping<string, ComparisonUnitAtom> item in revisions)
+                        {
+                            sb.Append(item.Key + Environment.NewLine);
+                        }
+
+                        string sbs = sb.ToString();
+                        TestUtil.NotePad(sbs);
+                    }
+
+                    List<WmlComparerRevision> mainDocPartRevisionList = revisions
+                        .Select(rg =>
+                        {
+                            var rev = new WmlComparerRevision();
+                            if (rg.Key.StartsWith("Inserted"))
+                            {
+                                rev.RevisionType = WmlComparerRevisionType.Inserted;
+                            }
+                            else if (rg.Key.StartsWith("Deleted"))
+                            {
+                                rev.RevisionType = WmlComparerRevisionType.Deleted;
+                            }
+
+                            XElement revTrackElement = rg.First().RevTrackElement;
+                            rev.RevisionXElement = revTrackElement;
+                            rev.Author = (string) revTrackElement.Attribute(W.author);
+                            rev.ContentXElement = rg.First().ContentElement;
+                            rev.Date = (string) revTrackElement.Attribute(W.date);
+                            rev.PartUri = wDoc.MainDocumentPart.Uri;
+                            rev.PartContentType = wDoc.MainDocumentPart.ContentType;
+
+                            if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name))
+                            {
+                                rev.Text = rg
+                                    .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value)
+                                    .StringConcatenate();
+                            }
+
+                            return rev;
+                        })
+                        .ToList();
+
+                    IEnumerable<WmlComparerRevision> footnotesRevisionList =
+                        GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.FootnotesPart, W.footnote, settings);
+                    IEnumerable<WmlComparerRevision> endnotesRevisionList =
+                        GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.EndnotesPart, W.endnote, settings);
+
+                    List<WmlComparerRevision> finalRevisionList = mainDocPartRevisionList
+                        .Concat(footnotesRevisionList)
+                        .Concat(endnotesRevisionList)
+                        .ToList();
+
+                    return finalRevisionList;
+                }
+            }
+        }
+
+        private static IEnumerable<WmlComparerRevision> GetFootnoteEndnoteRevisionList(
+            OpenXmlPart footnotesEndnotesPart,
+            XName footnoteEndnoteElementName,
+            WmlComparerSettings settings)
+        {
+            if (footnotesEndnotesPart == null)
+            {
+                return Enumerable.Empty<WmlComparerRevision>();
+            }
+
+            XDocument xDoc = footnotesEndnotesPart.GetXDocument();
+            IEnumerable<XElement> footnotesEndnotes =
+                xDoc.Root?.Elements(footnoteEndnoteElementName) ?? throw new OpenXmlPowerToolsException("Invalid document.");
+
+            var revisionsForPart = new List<WmlComparerRevision>();
+            foreach (XElement fn in footnotesEndnotes)
+            {
+                ComparisonUnitAtom[] atomList = CreateComparisonUnitAtomList(footnotesEndnotesPart, fn, settings).ToArray();
+
+                if (False)
+                {
+                    var sb = new StringBuilder();
+                    foreach (ComparisonUnitAtom item in atomList)
+                    {
+                        sb.Append(item + Environment.NewLine);
+                    }
+
+                    string sbs = sb.ToString();
+                    TestUtil.NotePad(sbs);
+                }
+
+                List<IGrouping<string, ComparisonUnitAtom>> grouped = atomList
+                    .GroupAdjacent(a =>
+                    {
+                        string key = a.CorrelationStatus.ToString();
+                        if (a.CorrelationStatus != CorrelationStatus.Equal)
+                        {
+                            var rt = new XElement(a.RevTrackElement.Name,
+                                new XAttribute(XNamespace.Xmlns + "w",
+                                    "http://schemas.openxmlformats.org/wordprocessingml/2006/main"),
+                                a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid));
+
+                            key += rt.ToString(SaveOptions.DisableFormatting);
+                        }
+
+                        return key;
+                    })
+                    .ToList();
+
+                List<IGrouping<string, ComparisonUnitAtom>> revisions = grouped
+                    .Where(k => k.Key != "Equal")
+                    .ToList();
+
+                IEnumerable<WmlComparerRevision> thisNoteRevisionList = revisions
+                    .Select(rg =>
+                    {
+                        var rev = new WmlComparerRevision();
+                        if (rg.Key.StartsWith("Inserted"))
+                        {
+                            rev.RevisionType = WmlComparerRevisionType.Inserted;
+                        }
+                        else if (rg.Key.StartsWith("Deleted"))
+                        {
+                            rev.RevisionType = WmlComparerRevisionType.Deleted;
+                        }
+
+                        XElement revTrackElement = rg.First().RevTrackElement;
+                        rev.RevisionXElement = revTrackElement;
+                        rev.Author = (string) revTrackElement.Attribute(W.author);
+                        rev.ContentXElement = rg.First().ContentElement;
+                        rev.Date = (string) revTrackElement.Attribute(W.date);
+                        rev.PartUri = footnotesEndnotesPart.Uri;
+                        rev.PartContentType = footnotesEndnotesPart.ContentType;
+
+                        if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name))
+                        {
+                            rev.Text = rg
+                                .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value)
+                                .StringConcatenate();
+                        }
+
+                        return rev;
+                    });
+
+                revisionsForPart.AddRange(thisNoteRevisionList);
+            }
+
+            return revisionsForPart;
+        }
+    }
+}

+ 29 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparer.Public.NestedTypes.cs

@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Xml.Linq;
+
+namespace OpenXmlPowerTools
+{
+    public static partial class WmlComparer
+    {
+        public class WmlComparerRevision
+        {
+            public WmlComparerRevisionType RevisionType;
+            public string Text;
+            public string Author;
+            public string Date;
+            public XElement ContentXElement;
+            public XElement RevisionXElement;
+            public Uri PartUri;
+            public string PartContentType;
+        }
+
+        public enum WmlComparerRevisionType
+        {
+            Inserted,
+            Deleted
+        }
+    }
+}

+ 10 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerConsolidateSettings.cs

@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace OpenXmlPowerTools
+{
+    public class WmlComparerConsolidateSettings
+    {
+        public bool ConsolidateWithTable = true;
+    }
+}

+ 27 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerExtensions.cs

@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public static class WmlComparerExtensions
+    {
+        public static XElement GetMainDocumentBody(this WordprocessingDocument wordDocument)
+        {
+            return wordDocument.GetMainDocumentRoot().Element(W.body) ?? throw new ArgumentException("Invalid document.");
+        }
+
+        public static XElement GetMainDocumentRoot(this WordprocessingDocument wordDocument)
+        {
+            return wordDocument.MainDocumentPart?.GetXElement() ?? throw new ArgumentException("Invalid document.");
+        }
+
+        public static XElement GetXElement(this OpenXmlPart part)
+        {
+            return part.GetXDocument()?.Root ?? throw new ArgumentException("Invalid document.");
+        }
+    }
+}

+ 29 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerSettings.cs

@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Globalization;
+using System.IO;
+
+namespace OpenXmlPowerTools
+{
+    public class WmlComparerSettings
+    {
+        public char[] WordSeparators;
+        public string AuthorForRevisions = "Open-Xml-PowerTools";
+        public string DateTimeForRevisions = DateTime.Now.ToString("o");
+        public double DetailThreshold = 0.15;
+        public bool CaseInsensitive = false;
+        public CultureInfo CultureInfo = null;
+        public Action<string> LogCallback = null;
+        public int StartingIdForFootnotesEndnotes = 1;
+
+        public DirectoryInfo DebugTempFileDi;
+
+        public WmlComparerSettings()
+        {
+            // note that , and . are processed explicitly to handle cases where they are in a number or word
+            WordSeparators = new[] { ' ', '-', ')', '(', ';', ',' }; // todo need to fix this for complete list
+        }
+    }
+}

+ 59 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlComparerUtil.cs

@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    internal static class WmlComparerUtil
+    {
+        public static string SHA1HashStringForUTF8String(string s)
+        {
+            var bytes = Encoding.UTF8.GetBytes(s);
+            var sha1 = SHA1.Create();
+            var hashBytes = sha1.ComputeHash(bytes);
+            return HexStringFromBytes(hashBytes);
+        }
+
+        public static string SHA1HashStringForByteArray(byte[] bytes)
+        {
+            var sha1 = SHA1.Create();
+            var hashBytes = sha1.ComputeHash(bytes);
+            return HexStringFromBytes(hashBytes);
+        }
+
+        public static string HexStringFromBytes(byte[] bytes)
+        {
+            var sb = new StringBuilder();
+            foreach (var b in bytes)
+            {
+                var hex = b.ToString("x2");
+                sb.Append(hex);
+            }
+
+            return sb.ToString();
+        }
+
+        public static ComparisonUnitGroupType ComparisonUnitGroupTypeFromLocalName(string localName)
+        {
+            switch (localName)
+            {
+                case "p":
+                    return ComparisonUnitGroupType.Paragraph;
+                case "tbl":
+                    return ComparisonUnitGroupType.Table;
+                case "tr":
+                    return ComparisonUnitGroupType.Row;
+                case "tc":
+                    return ComparisonUnitGroupType.Cell;
+                case "txbxContent":
+                    return ComparisonUnitGroupType.Textbox;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(localName),
+                        $@"Unsupported localName: '{localName}'.");
+            }
+        }
+    }
+}

+ 14 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/Comparer/WmlRevisedDocumentInfo.cs

@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Drawing;
+
+namespace OpenXmlPowerTools
+{
+    public class WmlRevisedDocumentInfo
+    {
+        public WmlDocument RevisedDocument;
+        public string Revisor;
+        public Color Color;
+    }
+}

+ 857 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/DocumentAssembler.cs

@@ -0,0 +1,857 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.XPath;
+using System.Xml.Schema;
+using DocumentFormat.OpenXml.Office.CustomUI;
+using DocumentFormat.OpenXml.Packaging;
+using OpenXmlPowerTools;
+using System.Collections;
+
+namespace OpenXmlPowerTools
+{
+    public class DocumentAssembler
+    {
+        public static WmlDocument AssembleDocument(WmlDocument templateDoc, XmlDocument data, out bool templateError)
+        {
+            XDocument xDoc = data.GetXDocument();
+            return AssembleDocument(templateDoc, xDoc.Root, out templateError);
+        }
+
+        public static WmlDocument AssembleDocument(WmlDocument templateDoc, XElement data, out bool templateError)
+        {
+            byte[] byteArray = templateDoc.DocumentByteArray;
+            using (MemoryStream mem = new MemoryStream())
+            {
+                mem.Write(byteArray, 0, (int)byteArray.Length);
+                using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true))
+                {
+                    if (RevisionAccepter.HasTrackedRevisions(wordDoc))
+                        throw new OpenXmlPowerToolsException("Invalid DocumentAssembler template - contains tracked revisions");
+
+                    var te = new TemplateError();
+                    foreach (var part in wordDoc.ContentParts())
+                    {
+                        ProcessTemplatePart(data, te, part);
+                    }
+                    templateError = te.HasError;
+                }
+                WmlDocument assembledDocument = new WmlDocument("TempFileName.docx", mem.ToArray());
+                return assembledDocument;
+            }
+        }
+
+        private static void ProcessTemplatePart(XElement data, TemplateError te, OpenXmlPart part)
+        {
+            XDocument xDoc = part.GetXDocument();
+
+            var xDocRoot = RemoveGoBackBookmarks(xDoc.Root);
+
+            // content controls in cells can surround the W.tc element, so transform so that such content controls are within the cell content
+            xDocRoot = (XElement)NormalizeContentControlsInCells(xDocRoot);
+
+            xDocRoot = (XElement)TransformToMetadata(xDocRoot, data, te);
+
+            // Table might have been placed at run-level, when it should be at block-level, so fix this.
+            // Repeat, EndRepeat, Conditional, EndConditional are allowed at run level, but only if there is a matching pair
+            // if there is only one Repeat, EndRepeat, Conditional, EndConditional, then move to block level.
+            // if there is a matching pair, then is OK.
+            xDocRoot = (XElement)ForceBlockLevelAsAppropriate(xDocRoot, te);
+
+            NormalizeTablesRepeatAndConditional(xDocRoot, te);
+
+            // any EndRepeat, EndConditional that remain are orphans, so replace with an error
+            ProcessOrphanEndRepeatEndConditional(xDocRoot, te);
+
+            // do the actual content replacement
+            xDocRoot = (XElement)ContentReplacementTransform(xDocRoot, data, te);
+
+            xDoc.Elements().First().ReplaceWith(xDocRoot);
+            part.PutXDocument();
+            return;
+        }
+
+        private static XName[] s_MetaToForceToBlock = new XName[] {
+            PA.Conditional,
+            PA.EndConditional,
+            PA.Repeat,
+            PA.EndRepeat,
+            PA.Table,
+        };
+
+        private static object ForceBlockLevelAsAppropriate(XNode node, TemplateError te)
+        {
+            XElement element = node as XElement;
+            if (element != null)
+            {
+                if (element.Name == W.p)
+                {
+                    var childMeta = element.Elements().Where(n => s_MetaToForceToBlock.Contains(n.Name)).ToList();
+                    if (childMeta.Count() == 1)
+                    {
+                        var child = childMeta.First();
+                        var otherTextInParagraph = element.Elements(W.r).Elements(W.t).Select(t => (string)t).StringConcatenate().Trim();
+                        if (otherTextInParagraph != "")
+                        {
+                            var newPara = new XElement(element);
+                            var newMeta = newPara.Elements().Where(n => s_MetaToForceToBlock.Contains(n.Name)).First();
+                            newMeta.ReplaceWith(CreateRunErrorMessage("Error: Unmatched metadata can't be in paragraph with other text", te));
+                            return newPara;
+                        }
+                        var meta = new XElement(child.Name,
+                            child.Attributes(),
+                            new XElement(W.p,
+                                element.Attributes(),
+                                element.Elements(W.pPr),
+                                child.Elements()));
+                        return meta;
+                    }
+                    var count = childMeta.Count();
+                    if (count % 2 == 0)
+                    {
+                        if (childMeta.Where(c => c.Name == PA.Repeat).Count() != childMeta.Where(c => c.Name == PA.EndRepeat).Count())
+                            return CreateContextErrorMessage(element, "Error: Mismatch Repeat / EndRepeat at run level", te);
+                        if (childMeta.Where(c => c.Name == PA.Conditional).Count() != childMeta.Where(c => c.Name == PA.EndConditional).Count())
+                            return CreateContextErrorMessage(element, "Error: Mismatch Conditional / EndConditional at run level", te);
+                        return new XElement(element.Name,
+                            element.Attributes(),
+                            element.Nodes().Select(n => ForceBlockLevelAsAppropriate(n, te)));
+                    }
+                    else
+                    {
+                        return CreateContextErrorMessage(element, "Error: Invalid metadata at run level", te);
+                    }
+                }
+                return new XElement(element.Name,
+                    element.Attributes(),
+                    element.Nodes().Select(n => ForceBlockLevelAsAppropriate(n, te)));
+            }
+            return node;
+        }
+
+        private static void ProcessOrphanEndRepeatEndConditional(XElement xDocRoot, TemplateError te)
+        {
+            foreach (var element in xDocRoot.Descendants(PA.EndRepeat).ToList())
+            {
+                var error = CreateContextErrorMessage(element, "Error: EndRepeat without matching Repeat", te);
+                element.ReplaceWith(error);
+            }
+            foreach (var element in xDocRoot.Descendants(PA.EndConditional).ToList())
+            {
+                var error = CreateContextErrorMessage(element, "Error: EndConditional without matching Conditional", te);
+                element.ReplaceWith(error);
+            }
+        }
+
+        private static XElement RemoveGoBackBookmarks(XElement xElement)
+        {
+            var cloneXDoc = new XElement(xElement);
+            while (true)
+            {
+                var bm = cloneXDoc.DescendantsAndSelf(W.bookmarkStart).FirstOrDefault(b => (string)b.Attribute(W.name) == "_GoBack");
+                if (bm == null)
+                    break;
+                var id = (string)bm.Attribute(W.id);
+                var endBm = cloneXDoc.DescendantsAndSelf(W.bookmarkEnd).FirstOrDefault(b => (string)b.Attribute(W.id) == id);
+                bm.Remove();
+                endBm.Remove();
+            }
+            return cloneXDoc;
+        }
+
+        // this transform inverts content controls that surround W.tc elements.  After transforming, the W.tc will contain
+        // the content control, which contains the paragraph content of the cell.
+        private static object NormalizeContentControlsInCells(XNode node)
+        {
+            XElement element = node as XElement;
+            if (element != null)
+            {
+                if (element.Name == W.sdt && element.Parent.Name == W.tr)
+                {
+                    var newCell = new XElement(W.tc,
+                        element.Elements(W.tc).Elements(W.tcPr),
+                        new XElement(W.sdt,
+                            element.Elements(W.sdtPr),
+                            element.Elements(W.sdtEndPr),
+                            new XElement(W.sdtContent,
+                                element.Elements(W.sdtContent).Elements(W.tc).Elements().Where(e => e.Name != W.tcPr))));
+                    return newCell;
+                }
+                return new XElement(element.Name,
+                    element.Attributes(),
+                    element.Nodes().Select(n => NormalizeContentControlsInCells(n)));
+            }
+            return node;
+        }
+
+        // The following method is written using tree modification, not RPFT, because it is easier to write in this fashion.
+        // These types of operations are not as easy to write using RPFT.
+        // Unless you are completely clear on the semantics of LINQ to XML DML, do not make modifications to this method.
+        private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateError te)
+        {
+            var tables = xDoc.Descendants(PA.Table).ToList();
+            foreach (var table in tables)
+            {
+                var followingElement = table.ElementsAfterSelf().Where(e => e.Name == W.tbl || e.Name == W.p).FirstOrDefault();
+                if (followingElement == null || followingElement.Name != W.tbl)
+                {
+                    table.ReplaceWith(CreateParaErrorMessage("Table metadata is not immediately followed by a table", te));
+                    continue;
+                }
+                // remove superflous paragraph from Table metadata
+                table.RemoveNodes();
+                // detach w:tbl from parent, and add to Table metadata
+                followingElement.Remove();
+                table.Add(followingElement);
+            }
+
+            int repeatDepth = 0;
+            int conditionalDepth = 0;
+            foreach (var metadata in xDoc.Descendants().Where(d =>
+                    d.Name == PA.Repeat ||
+                    d.Name == PA.Conditional ||
+                    d.Name == PA.EndRepeat ||
+                    d.Name == PA.EndConditional))
+            {
+                if (metadata.Name == PA.Repeat)
+                {
+                    ++repeatDepth;
+                    metadata.Add(new XAttribute(PA.Depth, repeatDepth));
+                    continue;
+                }
+                if (metadata.Name == PA.EndRepeat)
+                {
+                    metadata.Add(new XAttribute(PA.Depth, repeatDepth));
+                    --repeatDepth;
+                    continue;
+                }
+                if (metadata.Name == PA.Conditional)
+                {
+                    ++conditionalDepth;
+                    metadata.Add(new XAttribute(PA.Depth, conditionalDepth));
+                    continue;
+                }
+                if (metadata.Name == PA.EndConditional)
+                {
+                    metadata.Add(new XAttribute(PA.Depth, conditionalDepth));
+                    --conditionalDepth;
+                    continue;
+                }
+            }
+
+            while (true)
+            {
+                bool didReplace = false;
+                foreach (var metadata in xDoc.Descendants().Where(d => (d.Name == PA.Repeat || d.Name == PA.Conditional) && d.Attribute(PA.Depth) != null).ToList())
+                {
+                    var depth = (int)metadata.Attribute(PA.Depth);
+                    XName matchingEndName = null;
+                    if (metadata.Name == PA.Repeat)
+                        matchingEndName = PA.EndRepeat;
+                    else if (metadata.Name == PA.Conditional)
+                        matchingEndName = PA.EndConditional;
+                    if (matchingEndName == null)
+                        throw new OpenXmlPowerToolsException("Internal error");
+                    var matchingEnd = metadata.ElementsAfterSelf(matchingEndName).FirstOrDefault(end => { return (int)end.Attribute(PA.Depth) == depth; });
+                    if (matchingEnd == null)
+                    {
+                        metadata.ReplaceWith(CreateParaErrorMessage(string.Format("{0} does not have matching {1}", metadata.Name.LocalName, matchingEndName.LocalName), te));
+                        continue;
+                    }
+                    metadata.RemoveNodes();
+                    var contentBetween = metadata.ElementsAfterSelf().TakeWhile(after => after != matchingEnd).ToList();
+                    foreach (var item in contentBetween)
+                        item.Remove();
+                    contentBetween = contentBetween.Where(n => n.Name != W.bookmarkStart && n.Name != W.bookmarkEnd).ToList();
+                    metadata.Add(contentBetween);
+                    metadata.Attributes(PA.Depth).Remove();
+                    matchingEnd.Remove();
+                    didReplace = true;
+                    break;
+                }
+                if (!didReplace)
+                    break;
+            }
+        }
+
+        private static List<string> s_AliasList = new List<string>()
+        {
+            "Content",
+            "Table",
+            "Repeat",
+            "EndRepeat",
+            "Conditional",
+            "EndConditional",
+        };
+
+        private static object TransformToMetadata(XNode node, XElement data, TemplateError te)
+        {
+            XElement element = node as XElement;
+            if (element != null)
+            {
+                if (element.Name == W.sdt)
+                {
+                    var alias = (string)element.Elements(W.sdtPr).Elements(W.alias).Attributes(W.val).FirstOrDefault();
+                    if (alias == null || alias == "" || s_AliasList.Contains(alias))
+                    {
+                        var ccContents = element
+                            .DescendantsTrimmed(W.txbxContent)
+                            .Where(e => e.Name == W.t)
+                            .Select(t => (string)t)
+                            .StringConcatenate()
+                            .Trim()
+                            .Replace('“', '"')
+                            .Replace('”', '"');
+                        if (ccContents.StartsWith("<"))
+                        {
+                            XElement xml = TransformXmlTextToMetadata(te, ccContents);
+                            if (xml.Name == W.p || xml.Name == W.r)  // this means there was an error processing the XML.
+                            {
+                                if (element.Parent.Name == W.p)
+                                    return xml.Elements(W.r);
+                                return xml;
+                            }
+                            if (alias != null && xml.Name.LocalName != alias)
+                            {
+                                if (element.Parent.Name == W.p)
+                                    return CreateRunErrorMessage("Error: Content control alias does not match metadata element name", te);
+                                else
+                                    return CreateParaErrorMessage("Error: Content control alias does not match metadata element name", te);
+                            }
+                            xml.Add(element.Elements(W.sdtContent).Elements());
+                            return xml;
+                        }
+                        return new XElement(element.Name,
+                            element.Attributes(),
+                            element.Nodes().Select(n => TransformToMetadata(n, data, te)));
+                    }
+                    return new XElement(element.Name,
+                        element.Attributes(),
+                        element.Nodes().Select(n => TransformToMetadata(n, data, te)));
+                }
+                if (element.Name == W.p)
+                {
+                    var paraContents = element
+                        .DescendantsTrimmed(W.txbxContent)
+                        .Where(e => e.Name == W.t)
+                        .Select(t => (string)t)
+                        .StringConcatenate()
+                        .Trim();
+                    int occurances = paraContents.Select((c, i) => paraContents.Substring(i)).Count(sub => sub.StartsWith("<#"));
+                    if (paraContents.StartsWith("<#") && paraContents.EndsWith("#>") && occurances == 1)
+                    {
+                        var xmlText = paraContents.Substring(2, paraContents.Length - 4).Trim();
+                        XElement xml = TransformXmlTextToMetadata(te, xmlText);
+                        if (xml.Name == W.p || xml.Name == W.r)
+                            return xml;
+                        xml.Add(element);
+                        return xml;
+                    }
+                    if (paraContents.Contains("<#"))
+                    {
+                        List<RunReplacementInfo> runReplacementInfo = new List<RunReplacementInfo>();
+                        var thisGuid = Guid.NewGuid().ToString();
+                        var r = new Regex("<#.*?#>");
+                        XElement xml = null;
+                        OpenXmlRegex.Replace(new[] { element }, r, thisGuid, (para, match) =>
+                        {
+                            var matchString = match.Value.Trim();
+                            var xmlText = matchString.Substring(2, matchString.Length - 4).Trim().Replace('“', '"').Replace('”', '"');
+                            try
+                            {
+                                xml = XElement.Parse(xmlText);
+                            }
+                            catch (XmlException e)
+                            {
+                                RunReplacementInfo rri = new RunReplacementInfo()
+                                {
+                                    Xml = null,
+                                    XmlExceptionMessage = "XmlException: " + e.Message,
+                                    SchemaValidationMessage = null,
+                                };
+                                runReplacementInfo.Add(rri);
+                                return true;
+                            }
+                            string schemaError = ValidatePerSchema(xml);
+                            if (schemaError != null)
+                            {
+                                RunReplacementInfo rri = new RunReplacementInfo()
+                                {
+                                    Xml = null,
+                                    XmlExceptionMessage = null,
+                                    SchemaValidationMessage = "Schema Validation Error: " + schemaError,
+                                };
+                                runReplacementInfo.Add(rri);
+                                return true;
+                            }
+                            RunReplacementInfo rri2 = new RunReplacementInfo()
+                            {
+                                Xml = xml,
+                                XmlExceptionMessage = null,
+                                SchemaValidationMessage = null,
+                            };
+                            runReplacementInfo.Add(rri2);
+                            return true;
+                        }, false);
+
+                        var newPara = new XElement(element);
+                        foreach (var rri in runReplacementInfo)
+                        {
+                            var runToReplace = newPara.Descendants(W.r).FirstOrDefault(rn => rn.Value == thisGuid && rn.Parent.Name != PA.Content);
+                            if (runToReplace == null)
+                                throw new OpenXmlPowerToolsException("Internal error");
+                            if (rri.XmlExceptionMessage != null)
+                                runToReplace.ReplaceWith(CreateRunErrorMessage(rri.XmlExceptionMessage, te));
+                            else if (rri.SchemaValidationMessage != null)
+                                runToReplace.ReplaceWith(CreateRunErrorMessage(rri.SchemaValidationMessage, te));
+                            else
+                            {
+                                var newXml = new XElement(rri.Xml);
+                                newXml.Add(runToReplace);
+                                runToReplace.ReplaceWith(newXml);
+                            }
+                        }
+                        var coalescedParagraph = WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(newPara);
+                        return coalescedParagraph;
+                    }
+                }
+
+                return new XElement(element.Name,
+                    element.Attributes(),
+                    element.Nodes().Select(n => TransformToMetadata(n, data, te)));
+            }
+            return node;
+        }
+
+        private static XElement TransformXmlTextToMetadata(TemplateError te, string xmlText)
+        {
+            XElement xml;
+            try
+            {
+                xml = XElement.Parse(xmlText);
+            }
+            catch (XmlException e)
+            {
+                return CreateParaErrorMessage("XmlException: " + e.Message, te);
+            }
+            string schemaError = ValidatePerSchema(xml);
+            if (schemaError != null)
+                return CreateParaErrorMessage("Schema Validation Error: " + schemaError, te);
+            return xml;
+        }
+
+        private class RunReplacementInfo
+        {
+            public XElement Xml;
+            public string XmlExceptionMessage;
+            public string SchemaValidationMessage;
+        }
+
+        private static string ValidatePerSchema(XElement element)
+        {
+            if (s_PASchemaSets == null)
+            {
+                s_PASchemaSets = new Dictionary<XName, PASchemaSet>()
+                {
+                    {
+                        PA.Content,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='Content'>
+                                    <xs:complexType>
+                                      <xs:attribute name='Select' type='xs:string' use='required' />
+                                      <xs:attribute name='Optional' type='xs:boolean' use='optional' />
+                                    </xs:complexType>
+                                  </xs:element>
+                                </xs:schema>",
+                        }
+                    },
+                    {
+                        PA.Table,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='Table'>
+                                    <xs:complexType>
+                                      <xs:attribute name='Select' type='xs:string' use='required' />
+                                    </xs:complexType>
+                                  </xs:element>
+                                </xs:schema>",
+                        }
+                    },
+                    {
+                        PA.Repeat,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='Repeat'>
+                                    <xs:complexType>
+                                      <xs:attribute name='Select' type='xs:string' use='required' />
+                                      <xs:attribute name='Optional' type='xs:boolean' use='optional' />
+                                    </xs:complexType>
+                                  </xs:element>
+                                </xs:schema>",
+                        }
+                    },
+                    {
+                        PA.EndRepeat,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='EndRepeat' />
+                                </xs:schema>",
+                        }
+                    },
+                    {
+                        PA.Conditional,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='Conditional'>
+                                    <xs:complexType>
+                                      <xs:attribute name='Select' type='xs:string' use='required' />
+                                      <xs:attribute name='Match' type='xs:string' use='optional' />
+                                      <xs:attribute name='NotMatch' type='xs:string' use='optional' />
+                                    </xs:complexType>
+                                  </xs:element>
+                                </xs:schema>",
+                        }
+                    },
+                    {
+                        PA.EndConditional,
+                        new PASchemaSet() {
+                            XsdMarkup =
+                              @"<xs:schema attributeFormDefault='unqualified' elementFormDefault='qualified' xmlns:xs='http://www.w3.org/2001/XMLSchema'>
+                                  <xs:element name='EndConditional' />
+                                </xs:schema>",
+                        }
+                    },
+                };
+                foreach (var item in s_PASchemaSets)
+                {
+                    var itemPAss = item.Value;
+                    XmlSchemaSet schemas = new XmlSchemaSet();
+                    schemas.Add("", XmlReader.Create(new StringReader(itemPAss.XsdMarkup)));
+                    itemPAss.SchemaSet = schemas;
+                }
+            }
+            if (!s_PASchemaSets.ContainsKey(element.Name))
+            {
+                return string.Format("Invalid XML: {0} is not a valid element", element.Name.LocalName);
+            }
+            var paSchemaSet = s_PASchemaSets[element.Name];
+            XDocument d = new XDocument(element);
+            string message = null;
+            d.Validate(paSchemaSet.SchemaSet, (sender, e) =>
+            {
+                if (message == null)
+                    message = e.Message;
+            }, true);
+            if (message != null)
+                return message;
+            return null;
+        }
+
+        private class PA
+        {
+            public static XName Content = "Content";
+            public static XName Table = "Table";
+            public static XName Repeat = "Repeat";
+            public static XName EndRepeat = "EndRepeat";
+            public static XName Conditional = "Conditional";
+            public static XName EndConditional = "EndConditional";
+
+            public static XName Select = "Select";
+            public static XName Optional = "Optional";
+            public static XName Match = "Match";
+            public static XName NotMatch = "NotMatch";
+            public static XName Depth = "Depth";
+        }
+
+        private class PASchemaSet
+        {
+            public string XsdMarkup;
+            public XmlSchemaSet SchemaSet;
+        }
+
+        private static Dictionary<XName, PASchemaSet> s_PASchemaSets = null;
+
+        private class TemplateError
+        {
+            public bool HasError = false;
+        }
+
+        static object ContentReplacementTransform(XNode node, XElement data, TemplateError templateError)
+        {
+            XElement element = node as XElement;
+            if (element != null)
+            {
+                if (element.Name == PA.Content)
+                {
+                    XElement para = element.Descendants(W.p).FirstOrDefault();
+                    XElement run = element.Descendants(W.r).FirstOrDefault();
+
+                    var xPath = (string) element.Attribute(PA.Select);
+                    var optionalString = (string) element.Attribute(PA.Optional);
+                    bool optional = (optionalString != null && optionalString.ToLower() == "true");
+
+                    string newValue;
+                    try
+                    {
+                        newValue = EvaluateXPathToString(data, xPath, optional);
+                    }
+                    catch (XPathException e)
+                    {
+                        return CreateContextErrorMessage(element, "XPathException: " + e.Message, templateError);
+                    }
+
+                    if (para != null)
+                    {
+
+                        XElement p = new XElement(W.p, para.Elements(W.pPr));
+                        foreach(string line in newValue.Split('\n'))
+                        {
+                            p.Add(new XElement(W.r,
+                                    para.Elements(W.r).Elements(W.rPr).FirstOrDefault(),
+                                (p.Elements().Count() > 1) ? new XElement(W.br) : null,
+                                new XElement(W.t, line)));
+                        }
+                        return p;
+                    }
+                    else
+                    {
+                        List<XElement> list = new List<XElement>();
+                        foreach(string line in newValue.Split('\n'))
+                        {
+                            list.Add(new XElement(W.r,
+                                run.Elements().Where(e => e.Name != W.t),
+                                (list.Count > 0) ? new XElement(W.br) : null,
+                                new XElement(W.t, line)));
+                        }
+                        return list;
+                    }
+                }
+                if (element.Name == PA.Repeat)
+                {
+                    string selector = (string)element.Attribute(PA.Select);
+                    var optionalString = (string)element.Attribute(PA.Optional);
+                    bool optional = (optionalString != null && optionalString.ToLower() == "true");
+
+                    IEnumerable<XElement> repeatingData;
+                    try
+                    {
+                        repeatingData = data.XPathSelectElements(selector);
+                    }
+                    catch (XPathException e)
+                    {
+                        return CreateContextErrorMessage(element, "XPathException: " + e.Message, templateError);
+                    }
+                    if (!repeatingData.Any())
+                    {
+                        if (optional)
+                        {
+                            return null;
+                            //XElement para = element.Descendants(W.p).FirstOrDefault();
+                            //if (para != null)
+                            //    return new XElement(W.p, new XElement(W.r));
+                            //else
+                            //    return new XElement(W.r);
+                        }
+                        return CreateContextErrorMessage(element, "Repeat: Select returned no data", templateError);
+                    }
+                    var newContent = repeatingData.Select(d =>
+                        {
+                            var content = element
+                                .Elements()
+                                .Select(e => ContentReplacementTransform(e, d, templateError))
+                                .ToList();
+                            return content;
+                        })
+                        .ToList();
+                    return newContent;
+                }
+                if (element.Name == PA.Table)
+                {
+                    IEnumerable<XElement> tableData;
+                    try
+                    {
+                        tableData = data.XPathSelectElements((string)element.Attribute(PA.Select));
+                    }
+                    catch (XPathException e)
+                    {
+                        return CreateContextErrorMessage(element, "XPathException: " + e.Message, templateError);
+                    }
+                    if (tableData.Count() == 0)
+                        return CreateContextErrorMessage(element, "Table Select returned no data", templateError);
+                    XElement table = element.Element(W.tbl);
+                    XElement protoRow = table.Elements(W.tr).Skip(1).FirstOrDefault();
+                    var footerRowsBeforeTransform = table
+                        .Elements(W.tr)
+                        .Skip(2)
+                        .ToList();
+                    var footerRows = footerRowsBeforeTransform
+                        .Select(x => ContentReplacementTransform(x, data, templateError))
+                        .ToList();
+                    if (protoRow == null)
+                        return CreateContextErrorMessage(element, string.Format("Table does not contain a prototype row"), templateError);
+                    protoRow.Descendants(W.bookmarkStart).Remove();
+                    protoRow.Descendants(W.bookmarkEnd).Remove();
+                    XElement newTable = new XElement(W.tbl,
+                        table.Elements().Where(e => e.Name != W.tr),
+                        table.Elements(W.tr).FirstOrDefault(),
+                        tableData.Select(d =>
+                            new XElement(W.tr,
+                                protoRow.Elements().Where(r => r.Name != W.tc),
+                                protoRow.Elements(W.tc)
+                                    .Select(tc =>
+                                    {
+                                        XElement paragraph = tc.Elements(W.p).FirstOrDefault();
+                                        XElement cellRun = paragraph.Elements(W.r).FirstOrDefault();
+                                        string xPath = paragraph.Value;
+                                        string newValue = null;
+                                        try
+                                        {
+                                            newValue = EvaluateXPathToString(d, xPath, false);
+                                        }
+                                        catch (XPathException e)
+                                        {
+                                            XElement errorCell = new XElement(W.tc,
+                                                tc.Elements().Where(z => z.Name != W.p),
+                                                new XElement(W.p,
+                                                    paragraph.Element(W.pPr),
+                                                    CreateRunErrorMessage(e.Message, templateError)));
+                                            return errorCell;
+                                        }
+
+                                        XElement newCell = new XElement(W.tc,
+                                                   tc.Elements().Where(z => z.Name != W.p),
+                                                   new XElement(W.p,
+                                                       paragraph.Element(W.pPr),
+                                                       new XElement(W.r,
+                                                           cellRun != null ? cellRun.Element(W.rPr) : new XElement(W.rPr),  //if the cell was empty there is no cellrun
+                                                           new XElement(W.t, newValue))));
+                                        return newCell;
+                                    }))),
+                                    footerRows
+                                    );
+                    return newTable;
+                }
+                if (element.Name == PA.Conditional)
+                {
+                    string xPath = (string)element.Attribute(PA.Select);
+                    var match = (string)element.Attribute(PA.Match);
+                    var notMatch = (string)element.Attribute(PA.NotMatch);
+
+                    if (match == null && notMatch == null)
+                        return CreateContextErrorMessage(element, "Conditional: Must specify either Match or NotMatch", templateError);
+                    if (match != null && notMatch != null)
+                        return CreateContextErrorMessage(element, "Conditional: Cannot specify both Match and NotMatch", templateError);
+
+                    string testValue = null; 
+                   
+                    try
+                    {
+                        testValue = EvaluateXPathToString(data, xPath, false);
+                    }
+	                catch (XPathException e)
+                    {
+                        return CreateContextErrorMessage(element, e.Message, templateError);
+                    }
+                  
+                    if ((match != null && testValue == match) || (notMatch != null && testValue != notMatch))
+                    {
+                        var content = element.Elements().Select(e => ContentReplacementTransform(e, data, templateError));
+                        return content;
+                    }
+                    return null;
+                }
+                return new XElement(element.Name,
+                    element.Attributes(),
+                    element.Nodes().Select(n => ContentReplacementTransform(n, data, templateError)));
+            }
+            return node;
+        }
+
+        private static object CreateContextErrorMessage(XElement element, string errorMessage, TemplateError templateError)
+        {
+            XElement para = element.Descendants(W.p).FirstOrDefault();
+            XElement run = element.Descendants(W.r).FirstOrDefault();
+            var errorRun = CreateRunErrorMessage(errorMessage, templateError);
+            if (para != null)
+                return new XElement(W.p, errorRun);
+            else
+                return errorRun;
+        }
+
+        private static XElement CreateRunErrorMessage(string errorMessage, TemplateError templateError)
+        {
+            templateError.HasError = true;
+            var errorRun = new XElement(W.r,
+                new XElement(W.rPr,
+                    new XElement(W.color, new XAttribute(W.val, "FF0000")),
+                    new XElement(W.highlight, new XAttribute(W.val, "yellow"))),
+                    new XElement(W.t, errorMessage));
+            return errorRun;
+        }
+
+        private static XElement CreateParaErrorMessage(string errorMessage, TemplateError templateError)
+        {
+            templateError.HasError = true;
+            var errorPara = new XElement(W.p,
+                new XElement(W.r,
+                    new XElement(W.rPr,
+                        new XElement(W.color, new XAttribute(W.val, "FF0000")),
+                        new XElement(W.highlight, new XAttribute(W.val, "yellow"))),
+                        new XElement(W.t, errorMessage)));
+            return errorPara;
+        }
+
+        private static string EvaluateXPathToString(XElement element, string xPath, bool optional )
+        {
+            object xPathSelectResult;
+            try
+            {
+                //support some cells in the table may not have an xpath expression.
+                if (String.IsNullOrWhiteSpace(xPath)) return String.Empty;
+                
+                xPathSelectResult = element.XPathEvaluate(xPath);
+            }
+            catch (XPathException e)
+            {
+                throw new XPathException("XPathException: " + e.Message, e);
+            }
+
+            if ((xPathSelectResult is IEnumerable) && !(xPathSelectResult is string))
+            {
+                var selectedData = ((IEnumerable) xPathSelectResult).Cast<XObject>();
+                if (!selectedData.Any())
+                {
+                    if (optional) return string.Empty;
+                    throw new XPathException(string.Format("XPath expression ({0}) returned no results", xPath));
+                }
+                if (selectedData.Count() > 1)
+                {
+                    throw new XPathException(string.Format("XPath expression ({0}) returned more than one node", xPath));
+                }
+
+                XObject selectedDatum = selectedData.First(); 
+                
+                if (selectedDatum is XElement) return ((XElement) selectedDatum).Value;
+
+                if (selectedDatum is XAttribute) return ((XAttribute) selectedDatum).Value;
+            }
+
+            return xPathSelectResult.ToString();
+
+        }
+    }
+}

File diff suppressed because it is too large
+ 4038 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/DocumentBuilder.cs


+ 833 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/ExcelFormula.cs

@@ -0,0 +1,833 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+/* created on 9/8/2012 9:28:14 AM from peg generator V1.0 using 'ExcelFormula.txt' as input*/
+
+using Peg.Base;
+using System;
+using System.IO;
+using System.Text;
+namespace ExcelFormula
+{
+      
+      enum EExcelFormula{Formula= 1, Expression= 2, InfixTerms= 3, PreAndPostTerm= 4, 
+                          Term= 5, RefInfixTerms= 6, RefTerm= 7, Constant= 8, RefConstant= 9, 
+                          ErrorConstant= 10, LogicalConstant= 11, NumericalConstant= 12, 
+                          SignificandPart= 13, WholeNumberPart= 14, FractionalPart= 15, 
+                          ExponentPart= 16, StringConstant= 17, StringCharacter= 18, HighCharacter= 19, 
+                          ArrayConstant= 20, ConstantListRows= 21, ConstantListRow= 22, 
+                          InfixOperator= 23, ValueInfixOperator= 24, RefInfixOperator= 25, 
+                          UnionOperator= 26, IntersectionOperator= 27, RangeOperator= 28, 
+                          PostfixOperator= 29, PrefixOperator= 30, CellReference= 31, LocalCellReference= 32, 
+                          ExternalCellReference= 33, BookPrefix= 34, BangReference= 35, 
+                          SheetRangeReference= 36, SingleSheetPrefix= 37, SingleSheetReference= 38, 
+                          SingleSheetArea= 39, SingleSheet= 40, SheetRange= 41, WorkbookIndex= 42, 
+                          SheetName= 43, SheetNameCharacter= 44, SheetNameSpecial= 45, 
+                          SheetNameBaseCharacter= 46, A1Reference= 47, A1Cell= 48, A1Area= 49, 
+                          A1Column= 50, A1AbsoluteColumn= 51, A1RelativeColumn= 52, A1Row= 53, 
+                          A1AbsoluteRow= 54, A1RelativeRow= 55, CellFunctionCall= 56, UserDefinedFunctionCall= 57, 
+                          UserDefinedFunctionName= 58, ArgumentList= 59, Argument= 60, 
+                          ArgumentExpression= 61, ArgumentInfixTerms= 62, ArgumentPreAndPostTerm= 63, 
+                          ArgumentTerm= 64, ArgumentRefInfixTerms= 65, ArgumentRefTerm= 66, 
+                          ArgumentInfixOperator= 67, RefArgumentInfixOperator= 68, NameReference= 69, 
+                          ExternalName= 70, BangName= 71, Name= 72, NameStartCharacter= 73, 
+                          NameCharacter= 74, StructureReference= 75, TableIdentifier= 76, 
+                          TableName= 77, IntraTableReference= 78, InnerReference= 79, Keyword= 80, 
+                          KeywordList= 81, ColumnRange= 82, Column= 83, SimpleColumnName= 84, 
+                          EscapeColumnCharacter= 85, UnescapedColumnCharacter= 86, AnyNoSpaceColumnCharacter= 87, 
+                          SpacedComma= 88, SpacedLBracket= 89, SpacedRBracket= 90, ws= 91};
+      class ExcelFormula : PegCharParser 
+      {
+        
+         #region Input Properties
+        public static EncodingClass encodingClass = EncodingClass.ascii;
+        public static UnicodeDetection unicodeDetection = UnicodeDetection.notApplicable;
+        #endregion Input Properties
+        #region Constructors
+        public ExcelFormula()
+            : base()
+        {
+            
+        }
+        public ExcelFormula(string src,TextWriter FerrOut)
+			: base(src,FerrOut)
+        {
+            
+        }
+        #endregion Constructors
+        #region Overrides
+        public override string GetRuleNameFromId(int id)
+        {
+            try
+            {
+                   EExcelFormula ruleEnum = (EExcelFormula)id;
+                    string s= ruleEnum.ToString();
+                    int val;
+                    if( int.TryParse(s,out val) ){
+                        return base.GetRuleNameFromId(id);
+                    }else{
+                        return s;
+                    }
+            }
+            catch (Exception)
+            {
+                return base.GetRuleNameFromId(id);
+            }
+        }
+        public override void GetProperties(out EncodingClass encoding, out UnicodeDetection detection)
+        {
+            encoding = encodingClass;
+            detection = unicodeDetection;
+        } 
+        #endregion Overrides
+		#region Grammar Rules
+        public bool Formula()    /*Formula: Expression (!./FATAL<"end of line expected">);*/
+        {
+
+           return And(()=>  
+                     Expression()
+                  && (    Not(()=> Any() ) || Fatal("end of line expected")) );
+		}
+        public bool Expression()    /*Expression: ws InfixTerms;*/
+        {
+
+           return And(()=>    ws() && InfixTerms() );
+		}
+        public bool InfixTerms()    /*InfixTerms: PreAndPostTerm (InfixOperator ws PreAndPostTerm)*;*/
+        {
+
+           return And(()=>  
+                     PreAndPostTerm()
+                  && OptRepeat(()=>    
+                      And(()=>      
+                               InfixOperator()
+                            && ws()
+                            && PreAndPostTerm() ) ) );
+		}
+        public bool PreAndPostTerm()    /*PreAndPostTerm: (PrefixOperator ws)* Term (PostfixOperator ws)*;*/
+        {
+
+           return And(()=>  
+                     OptRepeat(()=> And(()=>    PrefixOperator() && ws() ) )
+                  && Term()
+                  && OptRepeat(()=> And(()=>    PostfixOperator() && ws() ) ) );
+		}
+        public bool Term()    /*Term: (RefInfixTerms / '(' Expression ')' / Constant) ws;*/
+        {
+
+           return And(()=>  
+                     (    
+                         RefInfixTerms()
+                      || And(()=>    Char('(') && Expression() && Char(')') )
+                      || Constant())
+                  && ws() );
+		}
+        public bool RefInfixTerms()    /*RefInfixTerms: RefTerm (RefInfixOperator ws RefTerm)*;*/
+        {
+
+           return And(()=>  
+                     RefTerm()
+                  && OptRepeat(()=>    
+                      And(()=>    RefInfixOperator() && ws() && RefTerm() ) ) );
+		}
+        public bool RefTerm()    /*RefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall
+	/ NameReference / StructureReference;*/
+        {
+
+           return   
+                     And(()=>    
+                         Char('(')
+                      && ws()
+                      && RefInfixTerms()
+                      && Char(')') )
+                  || RefConstant()
+                  || CellFunctionCall()
+                  || CellReference()
+                  || UserDefinedFunctionCall()
+                  || NameReference()
+                  || StructureReference();
+		}
+        public bool Constant()    /*^^Constant: ErrorConstant / LogicalConstant / NumericalConstant / StringConstant / ArrayConstant;*/
+        {
+
+           return TreeNT((int)EExcelFormula.Constant,()=>
+                  
+                     ErrorConstant()
+                  || LogicalConstant()
+                  || NumericalConstant()
+                  || StringConstant()
+                  || ArrayConstant() );
+		}
+        public bool RefConstant()    /*RefConstant: '#REF!';*/
+        {
+
+           return Char('#','R','E','F','!');
+		}
+        public bool ErrorConstant()    /*ErrorConstant: RefConstant / '#DIV/0!' / '#N/A' / '#NAME?' / '#NULL!' / '#NUM!' / '#VALUE!' / '#GETTING_DATA';*/
+        {
+
+           return   
+                     RefConstant()
+                  || Char('#','D','I','V','/','0','!')
+                  || Char('#','N','/','A')
+                  || Char('#','N','A','M','E','?')
+                  || Char('#','N','U','L','L','!')
+                  || Char('#','N','U','M','!')
+                  || Char('#','V','A','L','U','E','!')
+                  || Char("#GETTING_DATA");
+		}
+        public bool LogicalConstant()    /*LogicalConstant: 'FALSE' / 'TRUE';*/
+        {
+
+           return     Char('F','A','L','S','E') || Char('T','R','U','E');
+		}
+        public bool NumericalConstant()    /*NumericalConstant: '-'? SignificandPart ExponentPart?;*/
+        {
+
+           return And(()=>  
+                     Option(()=> Char('-') )
+                  && SignificandPart()
+                  && Option(()=> ExponentPart() ) );
+		}
+        public bool SignificandPart()    /*SignificandPart: WholeNumberPart FractionalPart? / FractionalPart;*/
+        {
+
+           return   
+                     And(()=>    
+                         WholeNumberPart()
+                      && Option(()=> FractionalPart() ) )
+                  || FractionalPart();
+		}
+        public bool WholeNumberPart()    /*WholeNumberPart: [0-9]+;*/
+        {
+
+           return PlusRepeat(()=> In('0','9') );
+		}
+        public bool FractionalPart()    /*FractionalPart: '.' [0-9]*;*/
+        {
+
+           return And(()=>    Char('.') && OptRepeat(()=> In('0','9') ) );
+		}
+        public bool ExponentPart()    /*ExponentPart: 'E' ('+' / '-')? [0-9]*;*/
+        {
+
+           return And(()=>  
+                     Char('E')
+                  && Option(()=>     Char('+') || Char('-') )
+                  && OptRepeat(()=> In('0','9') ) );
+		}
+        public bool StringConstant()    /*StringConstant: '"' ('""'/StringCharacter)* '"';*/
+        {
+
+           return And(()=>  
+                     Char('"')
+                  && OptRepeat(()=>     Char('"','"') || StringCharacter() )
+                  && Char('"') );
+		}
+        public bool StringCharacter()    /*StringCharacter: [#-~] / '!' / ' ' / HighCharacter;*/
+        {
+
+           return   
+                     In('#','~')
+                  || Char('!')
+                  || Char(' ')
+                  || HighCharacter();
+		}
+        public bool HighCharacter()    /*HighCharacter: [#x80-#xFFFF];*/
+        {
+
+           return In('\u0080','\uffff');
+		}
+        public bool ArrayConstant()    /*^^ArrayConstant: '{' ConstantListRows '}';*/
+        {
+
+           return TreeNT((int)EExcelFormula.ArrayConstant,()=>
+                And(()=>    Char('{') && ConstantListRows() && Char('}') ) );
+		}
+        public bool ConstantListRows()    /*ConstantListRows: ConstantListRow (';' ConstantListRow)*;*/
+        {
+
+           return And(()=>  
+                     ConstantListRow()
+                  && OptRepeat(()=>    
+                      And(()=>    Char(';') && ConstantListRow() ) ) );
+		}
+        public bool ConstantListRow()    /*^^ConstantListRow: Constant (',' Constant)*;*/
+        {
+
+           return TreeNT((int)EExcelFormula.ConstantListRow,()=>
+                And(()=>  
+                     Constant()
+                  && OptRepeat(()=> And(()=>    Char(',') && Constant() ) ) ) );
+		}
+        public bool InfixOperator()    /*InfixOperator: RefInfixOperator / ValueInfixOperator;*/
+        {
+
+           return     RefInfixOperator() || ValueInfixOperator();
+		}
+        public bool ValueInfixOperator()    /*^^ValueInfixOperator: '<>' / '>=' / '<=' / '^' / '*' / '/' / '+' / '-' / '&' / '=' / '<' / '>';*/
+        {
+
+           return TreeNT((int)EExcelFormula.ValueInfixOperator,()=>
+                OneOfLiterals(optimizedLiterals0) );
+		}
+        public bool RefInfixOperator()    /*RefInfixOperator: RangeOperator / UnionOperator / IntersectionOperator;*/
+        {
+
+           return   
+                     RangeOperator()
+                  || UnionOperator()
+                  || IntersectionOperator();
+		}
+        public bool UnionOperator()    /*^^UnionOperator: ',';*/
+        {
+
+           return TreeNT((int)EExcelFormula.UnionOperator,()=>
+                Char(',') );
+		}
+        public bool IntersectionOperator()    /*^^IntersectionOperator: ' ';*/
+        {
+
+           return TreeNT((int)EExcelFormula.IntersectionOperator,()=>
+                Char(' ') );
+		}
+        public bool RangeOperator()    /*^^RangeOperator: ':';*/
+        {
+
+           return TreeNT((int)EExcelFormula.RangeOperator,()=>
+                Char(':') );
+		}
+        public bool PostfixOperator()    /*^^PostfixOperator: '%';*/
+        {
+
+           return TreeNT((int)EExcelFormula.PostfixOperator,()=>
+                Char('%') );
+		}
+        public bool PrefixOperator()    /*^^PrefixOperator: '+' / '-';*/
+        {
+
+           return TreeNT((int)EExcelFormula.PrefixOperator,()=>
+                    Char('+') || Char('-') );
+		}
+        public bool CellReference()    /*CellReference: ExternalCellReference / LocalCellReference;*/
+        {
+
+           return     ExternalCellReference() || LocalCellReference();
+		}
+        public bool LocalCellReference()    /*LocalCellReference: A1Reference;*/
+        {
+
+           return A1Reference();
+		}
+        public bool ExternalCellReference()    /*ExternalCellReference: BangReference / SheetRangeReference / SingleSheetReference;*/
+        {
+
+           return   
+                     BangReference()
+                  || SheetRangeReference()
+                  || SingleSheetReference();
+		}
+        public bool BookPrefix()    /*BookPrefix: WorkbookIndex '!';*/
+        {
+
+           return And(()=>    WorkbookIndex() && Char('!') );
+		}
+        public bool BangReference()    /*BangReference: '!' (A1Reference / '#REF!');*/
+        {
+
+           return And(()=>  
+                     Char('!')
+                  && (    A1Reference() || Char('#','R','E','F','!')) );
+		}
+        public bool SheetRangeReference()    /*SheetRangeReference: SheetRange '!' A1Reference;*/
+        {
+
+           return And(()=>    SheetRange() && Char('!') && A1Reference() );
+		}
+        public bool SingleSheetPrefix()    /*SingleSheetPrefix: SingleSheet '!';*/
+        {
+
+           return And(()=>    SingleSheet() && Char('!') );
+		}
+        public bool SingleSheetReference()    /*SingleSheetReference: SingleSheetPrefix (A1Reference / '#REF!');*/
+        {
+
+           return And(()=>  
+                     SingleSheetPrefix()
+                  && (    A1Reference() || Char('#','R','E','F','!')) );
+		}
+        public bool SingleSheetArea()    /*SingleSheetArea: SingleSheetPrefix A1Area;*/
+        {
+
+           return And(()=>    SingleSheetPrefix() && A1Area() );
+		}
+        public bool SingleSheet()    /*SingleSheet: WorkbookIndex? SheetName / '\'' WorkbookIndex? SheetNameSpecial '\'';*/
+        {
+
+           return   
+                     And(()=>    
+                         Option(()=> WorkbookIndex() )
+                      && SheetName() )
+                  || And(()=>    
+                         Char('\'')
+                      && Option(()=> WorkbookIndex() )
+                      && SheetNameSpecial()
+                      && Char('\'') );
+		}
+        public bool SheetRange()    /*SheetRange: WorkbookIndex? SheetName ':' SheetName / '\'' WorkbookIndex? SheetNameSpecial ':' SheetNameSpecial '\'';*/
+        {
+
+           return   
+                     And(()=>    
+                         Option(()=> WorkbookIndex() )
+                      && SheetName()
+                      && Char(':')
+                      && SheetName() )
+                  || And(()=>    
+                         Char('\'')
+                      && Option(()=> WorkbookIndex() )
+                      && SheetNameSpecial()
+                      && Char(':')
+                      && SheetNameSpecial()
+                      && Char('\'') );
+		}
+        public bool WorkbookIndex()    /*^^WorkbookIndex: '[' WholeNumberPart ']';*/
+        {
+
+           return TreeNT((int)EExcelFormula.WorkbookIndex,()=>
+                And(()=>    Char('[') && WholeNumberPart() && Char(']') ) );
+		}
+        public bool SheetName()    /*^^SheetName: SheetNameCharacter+;*/
+        {
+
+           return TreeNT((int)EExcelFormula.SheetName,()=>
+                PlusRepeat(()=> SheetNameCharacter() ) );
+		}
+        public bool SheetNameCharacter()    /*SheetNameCharacter: [A-Za-z0-9._] / HighCharacter;*/
+        {
+
+           return   
+                     (In('A','Z', 'a','z', '0','9')||OneOf("._"))
+                  || HighCharacter();
+		}
+        public bool SheetNameSpecial()    /*^^SheetNameSpecial: SheetNameBaseCharacter ('\'\''* SheetNameBaseCharacter)*;*/
+        {
+
+           return TreeNT((int)EExcelFormula.SheetNameSpecial,()=>
+                And(()=>  
+                     SheetNameBaseCharacter()
+                  && OptRepeat(()=>    
+                      And(()=>      
+                               OptRepeat(()=> Char('\'','\'') )
+                            && SheetNameBaseCharacter() ) ) ) );
+		}
+        public bool SheetNameBaseCharacter()    /*SheetNameBaseCharacter: [A-Za-z0-9!"#$%&()+,-.;<=>@^_`{|}~ ] / HighCharacter;*/
+        {
+
+           return     OneOf(optimizedCharset0) || HighCharacter();
+		}
+        public bool A1Reference()    /*^^A1Reference: (A1Column ':' A1Column) / (A1Row ':' A1Row) / A1Area / A1Cell;*/
+        {
+
+           return TreeNT((int)EExcelFormula.A1Reference,()=>
+                  
+                     And(()=>    A1Column() && Char(':') && A1Column() )
+                  || And(()=>    A1Row() && Char(':') && A1Row() )
+                  || A1Area()
+                  || A1Cell() );
+		}
+        public bool A1Cell()    /*A1Cell: A1Column A1Row !NameCharacter;*/
+        {
+
+           return And(()=>  
+                     A1Column()
+                  && A1Row()
+                  && Not(()=> NameCharacter() ) );
+		}
+        public bool A1Area()    /*A1Area: A1Cell ':' A1Cell;*/
+        {
+
+           return And(()=>    A1Cell() && Char(':') && A1Cell() );
+		}
+        public bool A1Column()    /*^^A1Column: A1AbsoluteColumn / A1RelativeColumn;*/
+        {
+
+           return TreeNT((int)EExcelFormula.A1Column,()=>
+                    A1AbsoluteColumn() || A1RelativeColumn() );
+		}
+        public bool A1AbsoluteColumn()    /*A1AbsoluteColumn: '$' A1RelativeColumn;*/
+        {
+
+           return And(()=>    Char('$') && A1RelativeColumn() );
+		}
+        public bool A1RelativeColumn()    /*A1RelativeColumn: 'XF' [A-D] / 'X' [A-E] [A-Z] / [A-W][A-Z][A-Z] / [A-Z][A-Z] / [A-Z];*/
+        {
+
+           return   
+                     And(()=>    Char('X','F') && In('A','D') )
+                  || And(()=>    Char('X') && In('A','E') && In('A','Z') )
+                  || And(()=>    In('A','W') && In('A','Z') && In('A','Z') )
+                  || And(()=>    In('A','Z') && In('A','Z') )
+                  || In('A','Z');
+		}
+        public bool A1Row()    /*^^A1Row: A1AbsoluteRow / A1RelativeRow;*/
+        {
+
+           return TreeNT((int)EExcelFormula.A1Row,()=>
+                    A1AbsoluteRow() || A1RelativeRow() );
+		}
+        public bool A1AbsoluteRow()    /*A1AbsoluteRow: '$' A1RelativeRow;*/
+        {
+
+           return And(()=>    Char('$') && A1RelativeRow() );
+		}
+        public bool A1RelativeRow()    /*A1RelativeRow: [1-9][0-9]*;*/
+        {
+
+           return And(()=>    In('1','9') && OptRepeat(()=> In('0','9') ) );
+		}
+        public bool CellFunctionCall()    /*^^CellFunctionCall: A1Cell '(' ArgumentList ')';*/
+        {
+
+           return TreeNT((int)EExcelFormula.CellFunctionCall,()=>
+                And(()=>  
+                     A1Cell()
+                  && Char('(')
+                  && ArgumentList()
+                  && Char(')') ) );
+		}
+        public bool UserDefinedFunctionCall()    /*^^UserDefinedFunctionCall: UserDefinedFunctionName '(' ArgumentList ')';*/
+        {
+
+           return TreeNT((int)EExcelFormula.UserDefinedFunctionCall,()=>
+                And(()=>  
+                     UserDefinedFunctionName()
+                  && Char('(')
+                  && ArgumentList()
+                  && Char(')') ) );
+		}
+        public bool UserDefinedFunctionName()    /*UserDefinedFunctionName: NameReference;*/
+        {
+
+           return NameReference();
+		}
+        public bool ArgumentList()    /*ArgumentList: Argument (',' Argument)*;*/
+        {
+
+           return And(()=>  
+                     Argument()
+                  && OptRepeat(()=> And(()=>    Char(',') && Argument() ) ) );
+		}
+        public bool Argument()    /*Argument: ArgumentExpression / ws;*/
+        {
+
+           return     ArgumentExpression() || ws();
+		}
+        public bool ArgumentExpression()    /*^^ArgumentExpression: ws ArgumentInfixTerms;*/
+        {
+
+           return TreeNT((int)EExcelFormula.ArgumentExpression,()=>
+                And(()=>    ws() && ArgumentInfixTerms() ) );
+		}
+        public bool ArgumentInfixTerms()    /*ArgumentInfixTerms: ArgumentPreAndPostTerm (ArgumentInfixOperator ws ArgumentPreAndPostTerm)*;*/
+        {
+
+           return And(()=>  
+                     ArgumentPreAndPostTerm()
+                  && OptRepeat(()=>    
+                      And(()=>      
+                               ArgumentInfixOperator()
+                            && ws()
+                            && ArgumentPreAndPostTerm() ) ) );
+		}
+        public bool ArgumentPreAndPostTerm()    /*ArgumentPreAndPostTerm: (PrefixOperator ws)* ArgumentTerm (PostfixOperator ws)*;*/
+        {
+
+           return And(()=>  
+                     OptRepeat(()=> And(()=>    PrefixOperator() && ws() ) )
+                  && ArgumentTerm()
+                  && OptRepeat(()=> And(()=>    PostfixOperator() && ws() ) ) );
+		}
+        public bool ArgumentTerm()    /*ArgumentTerm: (ArgumentRefInfixTerms / '(' Expression ')' / Constant) ws;*/
+        {
+
+           return And(()=>  
+                     (    
+                         ArgumentRefInfixTerms()
+                      || And(()=>    Char('(') && Expression() && Char(')') )
+                      || Constant())
+                  && ws() );
+		}
+        public bool ArgumentRefInfixTerms()    /*ArgumentRefInfixTerms: ArgumentRefTerm (RefArgumentInfixOperator ws ArgumentRefTerm)*;*/
+        {
+
+           return And(()=>  
+                     ArgumentRefTerm()
+                  && OptRepeat(()=>    
+                      And(()=>      
+                               RefArgumentInfixOperator()
+                            && ws()
+                            && ArgumentRefTerm() ) ) );
+		}
+        public bool ArgumentRefTerm()    /*ArgumentRefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall
+	/ NameReference / StructureReference;*/
+        {
+
+           return   
+                     And(()=>    
+                         Char('(')
+                      && ws()
+                      && RefInfixTerms()
+                      && Char(')') )
+                  || RefConstant()
+                  || CellFunctionCall()
+                  || CellReference()
+                  || UserDefinedFunctionCall()
+                  || NameReference()
+                  || StructureReference();
+		}
+        public bool ArgumentInfixOperator()    /*ArgumentInfixOperator: RefArgumentInfixOperator / ValueInfixOperator;*/
+        {
+
+           return     RefArgumentInfixOperator() || ValueInfixOperator();
+		}
+        public bool RefArgumentInfixOperator()    /*RefArgumentInfixOperator: RangeOperator / IntersectionOperator;*/
+        {
+
+           return     RangeOperator() || IntersectionOperator();
+		}
+        public bool NameReference()    /*^^NameReference: (ExternalName / Name) !'[';*/
+        {
+
+           return TreeNT((int)EExcelFormula.NameReference,()=>
+                And(()=>  
+                     (    ExternalName() || Name())
+                  && Not(()=> Char('[') ) ) );
+		}
+        public bool ExternalName()    /*ExternalName: BangName / (SingleSheetPrefix / BookPrefix) Name;*/
+        {
+
+           return   
+                     BangName()
+                  || And(()=>    
+                         (    SingleSheetPrefix() || BookPrefix())
+                      && Name() );
+		}
+        public bool BangName()    /*BangName: '!' Name;*/
+        {
+
+           return And(()=>    Char('!') && Name() );
+		}
+        public bool Name()    /*Name: NameStartCharacter NameCharacter*;*/
+        {
+
+           return And(()=>  
+                     NameStartCharacter()
+                  && OptRepeat(()=> NameCharacter() ) );
+		}
+        public bool NameStartCharacter()    /*NameStartCharacter: [_\\A-Za-z] / HighCharacter;*/
+        {
+
+           return   
+                     (In('A','Z', 'a','z')||OneOf("_\\"))
+                  || HighCharacter();
+		}
+        public bool NameCharacter()    /*NameCharacter: NameStartCharacter / [0-9] / '.' / '?' / HighCharacter;*/
+        {
+
+           return   
+                     NameStartCharacter()
+                  || In('0','9')
+                  || Char('.')
+                  || Char('?')
+                  || HighCharacter();
+		}
+        public bool StructureReference()    /*^^StructureReference: TableIdentifier? IntraTableReference;*/
+        {
+
+           return TreeNT((int)EExcelFormula.StructureReference,()=>
+                And(()=>  
+                     Option(()=> TableIdentifier() )
+                  && IntraTableReference() ) );
+		}
+        public bool TableIdentifier()    /*TableIdentifier: BookPrefix? TableName;*/
+        {
+
+           return And(()=>    Option(()=> BookPrefix() ) && TableName() );
+		}
+        public bool TableName()    /*TableName: Name;*/
+        {
+
+           return Name();
+		}
+        public bool IntraTableReference()    /*IntraTableReference: SpacedLBracket InnerReference SpacedRBracket / Keyword / '[' SimpleColumnName ']';*/
+        {
+
+           return   
+                     And(()=>    
+                         SpacedLBracket()
+                      && InnerReference()
+                      && SpacedRBracket() )
+                  || Keyword()
+                  || And(()=>    
+                         Char('[')
+                      && SimpleColumnName()
+                      && Char(']') );
+		}
+        public bool InnerReference()    /*InnerReference: (KeywordList SpacedComma)? ColumnRange / KeywordList;*/
+        {
+
+           return   
+                     And(()=>    
+                         Option(()=>      
+                            And(()=>    KeywordList() && SpacedComma() ) )
+                      && ColumnRange() )
+                  || KeywordList();
+		}
+        public bool Keyword()    /*Keyword: '[#All]' / '[#Data]' / '[#Headers]' / '[#Totals]' / '[#This Row]';*/
+        {
+
+           return   
+                     Char('[','#','A','l','l',']')
+                  || Char('[','#','D','a','t','a',']')
+                  || Char("[#Headers]")
+                  || Char("[#Totals]")
+                  || Char("[#This Row]");
+		}
+        public bool KeywordList()    /*KeywordList: '[#Headers]' SpacedComma '[#Data]' / '[#Data]' SpacedComma '[#Totals]' / Keyword;*/
+        {
+
+           return   
+                     And(()=>    
+                         Char("[#Headers]")
+                      && SpacedComma()
+                      && Char('[','#','D','a','t','a',']') )
+                  || And(()=>    
+                         Char('[','#','D','a','t','a',']')
+                      && SpacedComma()
+                      && Char("[#Totals]") )
+                  || Keyword();
+		}
+        public bool ColumnRange()    /*ColumnRange: Column (':' Column)?;*/
+        {
+
+           return And(()=>  
+                     Column()
+                  && Option(()=> And(()=>    Char(':') && Column() ) ) );
+		}
+        public bool Column()    /*Column: '[' ws SimpleColumnName ws ']' / SimpleColumnName;*/
+        {
+
+           return   
+                     And(()=>    
+                         Char('[')
+                      && ws()
+                      && SimpleColumnName()
+                      && ws()
+                      && Char(']') )
+                  || SimpleColumnName();
+		}
+        public bool SimpleColumnName()    /*SimpleColumnName: AnyNoSpaceColumnCharacter+ (ws AnyNoSpaceColumnCharacter+)*;*/
+        {
+
+           return And(()=>  
+                     PlusRepeat(()=> AnyNoSpaceColumnCharacter() )
+                  && OptRepeat(()=>    
+                      And(()=>      
+                               ws()
+                            && PlusRepeat(()=> AnyNoSpaceColumnCharacter() ) ) ) );
+		}
+        public bool EscapeColumnCharacter()    /*EscapeColumnCharacter: '\'' / '#' / '[' / ']';*/
+        {
+
+           return     Char('\'') || Char('#') || Char('[') || Char(']');
+		}
+        public bool UnescapedColumnCharacter()    /*UnescapedColumnCharacter: [A-Za-z0-9!"#$%&()*+,-./:;<=>?@\\^_`{|}~] / HighCharacter;*/
+        {
+
+           return     OneOf(optimizedCharset1) || HighCharacter();
+		}
+        public bool AnyNoSpaceColumnCharacter()    /*AnyNoSpaceColumnCharacter: ('\'' EscapeColumnCharacter) / UnescapedColumnCharacter;*/
+        {
+
+           return   
+                     And(()=>    Char('\'') && EscapeColumnCharacter() )
+                  || UnescapedColumnCharacter();
+		}
+        public bool SpacedComma()    /*SpacedComma: ' '? ',' ' '?;*/
+        {
+
+           return And(()=>  
+                     Option(()=> Char(' ') )
+                  && Char(',')
+                  && Option(()=> Char(' ') ) );
+		}
+        public bool SpacedLBracket()    /*SpacedLBracket: '[' ' '?;*/
+        {
+
+           return And(()=>    Char('[') && Option(()=> Char(' ') ) );
+		}
+        public bool SpacedRBracket()    /*SpacedRBracket: ' '? ']';*/
+        {
+
+           return And(()=>    Option(()=> Char(' ') ) && Char(']') );
+		}
+        public bool ws()    /*ws: ' '*;*/
+        {
+
+           return OptRepeat(()=> Char(' ') );
+		}
+		#endregion Grammar Rules
+
+        #region Optimization Data 
+        internal static OptimizedCharset optimizedCharset0;
+        internal static OptimizedCharset optimizedCharset1;
+        
+        internal static OptimizedLiterals optimizedLiterals0;
+        
+        static ExcelFormula()
+        {
+            {
+               OptimizedCharset.Range[] ranges = new OptimizedCharset.Range[]
+                  {new OptimizedCharset.Range('A','Z'),
+                   new OptimizedCharset.Range('a','z'),
+                   new OptimizedCharset.Range('0','9'),
+                   new OptimizedCharset.Range(',','.'),
+                   };
+               char[] oneOfChars = new char[]    {'!','"','#','$','%'
+                                                  ,'&','(',')','+',';'
+                                                  ,'<','=','>','@','^'
+                                                  ,'_','`','{','|','}'
+                                                  ,'~',' '};
+               optimizedCharset0= new OptimizedCharset(ranges,oneOfChars);
+            }
+            
+            {
+               OptimizedCharset.Range[] ranges = new OptimizedCharset.Range[]
+                  {new OptimizedCharset.Range('A','Z'),
+                   new OptimizedCharset.Range('a','z'),
+                   new OptimizedCharset.Range('0','9'),
+                   new OptimizedCharset.Range(',','.'),
+                   };
+               char[] oneOfChars = new char[]    {'!','"','#','$','%'
+                                                  ,'&','(',')','*','+'
+                                                  ,'/',':',';','<','='
+                                                  ,'>','?','@','\\','^'
+                                                  ,'_','`','{','|','}'
+                                                  ,'~'};
+               optimizedCharset1= new OptimizedCharset(ranges,oneOfChars);
+            }
+            
+            
+            {
+               string[] literals=
+               { "<>",">=","<=","^","*","/","+","-",
+                  "&","=","<",">" };
+               optimizedLiterals0= new OptimizedLiterals(literals);
+            }
+
+            
+        }
+        #endregion Optimization Data 
+           }
+}

+ 416 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/FieldRetriever.cs

@@ -0,0 +1,416 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+
+namespace OpenXmlPowerTools
+{
+    public class FieldRetriever
+    {
+        public static string InstrText(XElement root, int id)
+        {
+
+            XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
+
+#if false
+            // This is the old code.  Both versions work - the caching version is significantly faster.
+            var relevantElements = root.Descendants()
+                .Where(e =>
+                {
+                    Stack<FieldElementTypeInfo> s = e.Annotation<Stack<FieldElementTypeInfo>>();
+                    if (s != null)
+                        return s.Any(z => z.Id == id &&
+                            z.FieldElementType == FieldElementTypeEnum.InstrText);
+                    return false;
+                })
+                .ToList();
+#else
+            var cachedAnnotationInformation = root.Annotation<Dictionary<int, List<XElement>>>();
+            if (cachedAnnotationInformation == null)
+                throw new OpenXmlPowerToolsException("Internal error");
+
+            // it is possible that a field code contains no instr text
+            if (!cachedAnnotationInformation.ContainsKey(id))
+                return "";
+            var relevantElements = cachedAnnotationInformation[id];
+#endif
+
+            var groupedSubFields = relevantElements
+                .GroupAdjacent(e =>
+                {
+                    Stack<FieldElementTypeInfo> s = e.Annotation<Stack<FieldElementTypeInfo>>();
+                    var stackElement = s.FirstOrDefault(z => z.Id == id);
+                    var elementsBefore = s.TakeWhile(z => z != stackElement);
+                    return elementsBefore.Any();
+                })
+                .ToList();
+
+            var instrText = groupedSubFields
+                .Select(g =>
+                {
+                    if (g.Key == false)
+                    {
+                        return g.Select(e =>
+                        {
+                            Stack<FieldElementTypeInfo> s = e.Annotation<Stack<FieldElementTypeInfo>>();
+                            var stackElement = s.FirstOrDefault(z => z.Id == id);
+                            if (stackElement.FieldElementType == FieldElementTypeEnum.InstrText &&
+                                e.Name == w + "instrText")
+                                return e.Value;
+                            return "";
+                        })
+                            .StringConcatenate();
+                    }
+                    else
+                    {
+                        Stack<FieldElementTypeInfo> s = g.First().Annotation<Stack<FieldElementTypeInfo>>();
+                        var stackElement = s.FirstOrDefault(z => z.Id == id);
+                        var elementBefore = s.TakeWhile(z => z != stackElement).Last();
+                        var subFieldId = elementBefore.Id;
+                        return InstrText(root, subFieldId);
+                    }
+                })
+                .StringConcatenate();
+
+            return "{" + instrText + "}";
+        }
+
+        public static void AnnotateWithFieldInfo(OpenXmlPart part)
+        {
+            XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
+
+            XElement root = part.GetXDocument().Root;
+            var r = root.DescendantsAndSelf()
+                .Rollup(
+                    new FieldElementTypeStack
+                    {
+                        Id = 0,
+                        FiStack = null,
+                    },
+                    (e, s) =>
+                    {
+                        if (e.Name == w + "fldChar")
+                        {
+                            if (e.Attribute(w + "fldCharType").Value == "begin")
+                            {
+                                Stack<FieldElementTypeInfo> fis;
+                                if (s.FiStack == null)
+                                    fis = new Stack<FieldElementTypeInfo>();
+                                else
+                                    fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                                fis.Push(
+                                    new FieldElementTypeInfo
+                                    {
+                                        Id = s.Id + 1,
+                                        FieldElementType = FieldElementTypeEnum.Begin,
+                                    });
+                                return new FieldElementTypeStack
+                                {
+                                    Id = s.Id + 1,
+                                    FiStack = fis,
+                                };
+                            };
+                            if (e.Attribute(w + "fldCharType").Value == "separate")
+                            {
+                                Stack<FieldElementTypeInfo> fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                                FieldElementTypeInfo wfi = fis.Pop();
+                                fis.Push(
+                                    new FieldElementTypeInfo
+                                    {
+                                        Id = wfi.Id,
+                                        FieldElementType = FieldElementTypeEnum.Separate,
+                                    });
+                                return new FieldElementTypeStack
+                                {
+                                    Id = s.Id,
+                                    FiStack = fis,
+                                };
+                            }
+                            if (e.Attribute(w + "fldCharType").Value == "end")
+                            {
+                                Stack<FieldElementTypeInfo> fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                                FieldElementTypeInfo wfi = fis.Pop();
+                                return new FieldElementTypeStack
+                                {
+                                    Id = s.Id,
+                                    FiStack = fis,
+                                };
+                            }
+                        }
+                        if (s.FiStack == null || s.FiStack.Count() == 0)
+                            return s;
+                        FieldElementTypeInfo wfi3 = s.FiStack.Peek();
+                        if (wfi3.FieldElementType == FieldElementTypeEnum.Begin)
+                        {
+                            Stack<FieldElementTypeInfo> fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                            FieldElementTypeInfo wfi2 = fis.Pop();
+                            fis.Push(
+                                new FieldElementTypeInfo
+                                {
+                                    Id = wfi2.Id,
+                                    FieldElementType = FieldElementTypeEnum.InstrText,
+                                });
+                            return new FieldElementTypeStack
+                            {
+                                Id = s.Id,
+                                FiStack = fis,
+                            };
+                        }
+                        if (wfi3.FieldElementType == FieldElementTypeEnum.Separate)
+                        {
+                            Stack<FieldElementTypeInfo> fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                            FieldElementTypeInfo wfi2 = fis.Pop();
+                            fis.Push(
+                                new FieldElementTypeInfo
+                                {
+                                    Id = wfi2.Id,
+                                    FieldElementType = FieldElementTypeEnum.Result,
+                                });
+                            return new FieldElementTypeStack
+                            {
+                                Id = s.Id,
+                                FiStack = fis,
+                            };
+                        }
+                        if (wfi3.FieldElementType == FieldElementTypeEnum.End)
+                        {
+                            Stack<FieldElementTypeInfo> fis = new Stack<FieldElementTypeInfo>(s.FiStack.Reverse());
+                            fis.Pop();
+                            if (!fis.Any())
+                                fis = null;
+                            return new FieldElementTypeStack
+                            {
+                                Id = s.Id,
+                                FiStack = fis,
+                            };
+                        }
+                        return s;
+                    });
+            var elementPlusInfo = root.DescendantsAndSelf().PtZip(r, (t1, t2) =>
+            {
+                return new
+                {
+                    Element = t1,
+                    Id = t2.Id,
+                    WmlFieldInfoStack = t2.FiStack,
+                };
+            });
+            foreach (var item in elementPlusInfo)
+            {
+                if (item.WmlFieldInfoStack != null)
+                    item.Element.AddAnnotation(item.WmlFieldInfoStack);
+            }
+
+            //This code is useful when you want to take a look at the annotations, making sure that they are made correctly.
+            //
+            //foreach (var desc in root.DescendantsAndSelf())
+            //{
+            //    Stack<FieldElementTypeInfo> s = desc.Annotation<Stack<FieldElementTypeInfo>>();
+            //    if (s != null)
+            //    {
+            //        Console.WriteLine(desc.Name.LocalName.PadRight(20));
+            //        foreach (var item in s)
+            //        {
+            //            Console.WriteLine("    {0:0000} {1}", item.Id, item.FieldElementType.ToString());
+            //            Console.ReadKey();
+            //        }
+            //    }
+            //}
+
+            var cachedAnnotationInformation = new Dictionary<int, List<XElement>>();
+            foreach (var desc in root.DescendantsTrimmed(d => d.Name == W.rPr || d.Name == W.pPr))
+            {
+                Stack<FieldElementTypeInfo> s = desc.Annotation<Stack<FieldElementTypeInfo>>();
+
+                if (s != null )
+                {
+                    foreach (var item in s)
+                    {
+                        if (item.FieldElementType == FieldElementTypeEnum.InstrText)
+                        {
+                            if (cachedAnnotationInformation.ContainsKey(item.Id))
+                            {
+                                cachedAnnotationInformation[item.Id].Add(desc);
+                            }
+                            else
+                            {
+                                cachedAnnotationInformation.Add(item.Id, new List<XElement>() { desc });
+                            }
+                        }
+                    }
+                }
+            }
+            root.AddAnnotation(cachedAnnotationInformation);
+        }
+
+        private enum State
+        {
+            InToken,
+            InWhiteSpace,
+            InQuotedToken,
+            OnOpeningQuote,
+            OnClosingQuote,
+            OnBackslash,
+        }
+
+        private static string[] GetTokens(string field)
+        {
+            State state = State.InWhiteSpace;
+            int tokenStart = 0;
+            char quoteStart = char.MinValue;
+            List<string> tokens = new List<string>();
+            for (int c = 0; c < field.Length; c++)
+            {
+                if (Char.IsWhiteSpace(field[c]))
+                {
+                    if (state == State.InToken)
+                    {
+                        tokens.Add(field.Substring(tokenStart, c - tokenStart));
+                        state = State.InWhiteSpace;
+                        continue;
+                    }
+                    if (state == State.OnOpeningQuote)
+                    {
+                        tokenStart = c;
+                        state = State.InQuotedToken;
+                    }
+                    if (state == State.OnClosingQuote)
+                        state = State.InWhiteSpace;
+                    continue;
+                }
+                if (field[c] == '\\')
+                {
+                    if (state == State.InQuotedToken)
+                    {
+                        state = State.OnBackslash;
+                        continue;
+                    }
+                }
+                if (state == State.OnBackslash)
+                {
+                    state = State.InQuotedToken;
+                    continue;
+                }
+                if (field[c] == '"' || field[c] == '\'' || field[c] == 0x201d)
+                {
+                    if (state == State.InWhiteSpace)
+                    {
+                        quoteStart = field[c];
+                        state = State.OnOpeningQuote;
+                        continue;
+                    }
+                    if (state == State.InQuotedToken)
+                    {
+                        if (field[c] == quoteStart)
+                        {
+                            tokens.Add(field.Substring(tokenStart, c - tokenStart));
+                            state = State.OnClosingQuote;
+                            continue;
+                        }
+                        continue;
+                    }
+                    if (state == State.OnOpeningQuote)
+                    {
+                        if (field[c] == quoteStart)
+                        {
+                            state = State.OnClosingQuote;
+                            continue;
+                        }
+                        else
+                        {
+                            tokenStart = c;
+                            state = State.InQuotedToken;
+                            continue;
+                        }
+                    }
+                    continue;
+                }
+                if (state == State.InWhiteSpace)
+                {
+                    tokenStart = c;
+                    state = State.InToken;
+                    continue;
+                }
+                if (state == State.OnOpeningQuote)
+                {
+                    tokenStart = c;
+                    state = State.InQuotedToken;
+                    continue;
+                }
+                if (state == State.OnClosingQuote)
+                {
+                    tokenStart = c;
+                    state = State.InToken;
+                    continue;
+                }
+            }
+            if (state == State.InToken)
+                tokens.Add(field.Substring(tokenStart, field.Length - tokenStart));
+            return tokens.ToArray();
+        }
+
+        public static FieldInfo ParseField(string field)
+        {
+            FieldInfo emptyField = new FieldInfo
+            {
+                FieldType = "",
+                Arguments = new string[] { },
+                Switches = new string[] { },
+            };
+
+            if (field.Length == 0)
+                return emptyField;
+            string fieldType = field.TrimStart().Split(' ').FirstOrDefault();
+            if (fieldType == null)
+                return emptyField;
+            if (fieldType.ToUpper() != "HYPERLINK" &&
+                fieldType.ToUpper() != "REF" &&
+                fieldType.ToUpper() != "SEQ" &&
+                fieldType.ToUpper() != "STYLEREF")
+                return emptyField;
+            string[] tokens = GetTokens(field);
+            if (tokens.Length == 0)
+                return emptyField;
+            FieldInfo fieldInfo = new FieldInfo()
+            {
+                FieldType = tokens[0],
+                Switches = tokens.Where(t => t[0] == '\\').ToArray(),
+                Arguments = tokens.Skip(1).Where(t => t[0] != '\\').ToArray(),
+            };
+            return fieldInfo;
+        }
+
+        public class FieldInfo
+        {
+            public string FieldType;
+            public string[] Switches;
+            public string[] Arguments;
+        }
+
+        public enum FieldElementTypeEnum
+        {
+            Begin,
+            InstrText,
+            Separate,
+            Result,
+            End,
+        };
+
+        public class FieldElementTypeInfo
+        {
+            public int Id;
+            public FieldElementTypeEnum FieldElementType;
+        }
+
+        public class FieldElementTypeStack
+        {
+            public int Id;
+            public Stack<FieldElementTypeInfo> FiStack;
+        }
+    }
+}

File diff suppressed because it is too large
+ 3510 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/FormattingAssembler.cs


+ 240 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_Default.cs

@@ -0,0 +1,240 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    class ListItemTextGetter_Default
+    {
+        private static string[] RomanOnes =
+        {
+            "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
+        };
+
+        private static string[] RomanTens =
+        {
+            "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"
+        };
+
+        private static string[] RomanHundreds =
+        {
+            "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M"
+        };
+
+        private static string[] RomanThousands =
+        {
+            "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM",
+            "MMMMMMMMM", "MMMMMMMMMM"
+        };
+
+        private static string[] OneThroughNineteen = {
+            "one", "two", "three", "four", "five", "six", "seven", "eight",
+            "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
+            "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+        };
+
+        private static string[] Tens = {
+            "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
+            "eighty", "ninety"
+        };
+
+        private static string[] OrdinalOneThroughNineteen = {
+            "first", "second", "third", "fourth", "fifth", "sixth",
+            "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth",
+            "thirteenth", "fourteenth", "fifteenth", "sixteenth",
+            "seventeenth", "eighteenth", "nineteenth"
+        };
+
+        private static string[] OrdinalTenths = {
+            "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth",
+            "sixtieth", "seventieth", "eightieth", "ninetieth"
+        };
+
+        public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+        {
+            if (numFmt == "none")
+            {
+                return "";
+            }
+            if (numFmt == "decimal")
+            {
+                return levelNumber.ToString();
+            }
+            if (numFmt == "decimalZero")
+            {
+                if (levelNumber <= 9)
+                    return "0" + levelNumber.ToString();
+                else
+                    return levelNumber.ToString();
+            }
+            if (numFmt == "upperRoman")
+            {
+                int ones = levelNumber % 10;
+                int tens = (levelNumber % 100) / 10;
+                int hundreds = (levelNumber % 1000) / 100;
+                int thousands = levelNumber / 1000;
+                return RomanThousands[thousands] + RomanHundreds[hundreds] +
+                    RomanTens[tens] + RomanOnes[ones];
+            }
+            if (numFmt == "lowerRoman")
+            {
+                int ones = levelNumber % 10;
+                int tens = (levelNumber % 100) / 10;
+                int hundreds = (levelNumber % 1000) / 100;
+                int thousands = levelNumber / 1000;
+                return (RomanThousands[thousands] + RomanHundreds[hundreds] +
+                    RomanTens[tens] + RomanOnes[ones]).ToLower();
+            }
+            if (numFmt == "upperLetter")
+            {
+                int levelNumber2 = levelNumber % 780;
+                if (levelNumber2 == 0)
+                    levelNumber2 = 780;
+                string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+                int c = (levelNumber2 - 1) / 26;
+                int n = (levelNumber2 - 1) % 26;
+                char x = a[n];
+                return "".PadRight(c + 1, x);
+            }
+            if (numFmt == "lowerLetter")
+            {
+                int levelNumber3 = levelNumber % 780;
+                if (levelNumber3 == 0)
+                    levelNumber3 = 780;
+                string a = "abcdefghijklmnopqrstuvwxyz";
+                int c = (levelNumber3 - 1) / 26;
+                int n = (levelNumber3 - 1) % 26;
+                char x = a[n];
+                return "".PadRight(c + 1, x);
+            }
+            if (numFmt == "ordinal")
+            {
+                string suffix;
+                if (levelNumber % 100 == 11 || levelNumber % 100 == 12 ||
+                    levelNumber % 100 == 13)
+                    suffix = "th";
+                else if (levelNumber % 10 == 1)
+                    suffix = "st";
+                else if (levelNumber % 10 == 2)
+                    suffix = "nd";
+                else if (levelNumber % 10 == 3)
+                    suffix = "rd";
+                else
+                    suffix = "th";
+                return levelNumber.ToString() + suffix;
+            }
+            if (numFmt == "cardinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1)
+                    result += OneThroughNineteen[t1 - 1] + " thousand";
+                if (t1 >= 1 && t2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (t1 >= 1)
+                    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1)
+                    result += OneThroughNineteen[h1 - 1] + " hundred";
+                if (h1 >= 1 && h2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (h1 >= 1)
+                    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    result += Tens[x - 1];
+                    if (r >= 1)
+                        result += "-" + OneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            if (numFmt == "ordinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1 && t2 != 0)
+                    result += OneThroughNineteen[t1 - 1] + " thousand";
+                if (t1 >= 1 && t2 == 0)
+                {
+                    result += OneThroughNineteen[t1 - 1] + " thousandth";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                if (t1 >= 1)
+                    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1 && h2 != 0)
+                    result += OneThroughNineteen[h1 - 1] + " hundred";
+                if (h1 >= 1 && h2 == 0)
+                {
+                    result += OneThroughNineteen[h1 - 1] + " hundredth";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                if (h1 >= 1)
+                    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OrdinalOneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    if (r == 0)
+                        result += OrdinalTenths[x - 1];
+                    else
+                        result += Tens[x - 1];
+                    if (r >= 1)
+                        result += "-" + OrdinalOneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            if (numFmt == "01, 02, 03, ...")
+            {
+                return string.Format("{0:00}", levelNumber);
+            }
+            if (numFmt == "001, 002, 003, ...")
+            {
+                return string.Format("{0:000}", levelNumber);
+            }
+            if (numFmt == "0001, 0002, 0003, ...")
+            {
+                return string.Format("{0:0000}", levelNumber);
+            }
+            if (numFmt == "00001, 00002, 00003, ...")
+            {
+                return string.Format("{0:00000}", levelNumber);
+            }
+            if (numFmt == "bullet")
+                return "";
+            if (numFmt == "decimalEnclosedCircle")
+            {
+                if (levelNumber >= 1 && levelNumber <= 20)
+                {
+                    // 9311 + levelNumber
+                    var s = new string(new[] { (char)(9311 + levelNumber) });
+                    return s;
+                }
+                return levelNumber.ToString();
+            }
+            return levelNumber.ToString();
+        }
+    }
+}

+ 233 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_fr_FR.cs

@@ -0,0 +1,233 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    public class ListItemTextGetter_fr_FR
+    {
+        private static string[] OneThroughNineteen = {
+            "", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit",
+            "neuf", "dix", "onze", "douze", "treize", "quatorze",
+            "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"
+        };
+
+        private static string[] Tens = {
+            "", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix",
+            "quatre-vingt", "quatre-vingt-dix"
+        };
+
+        private static string[] OrdinalOneThroughNineteen = {
+            "", "unième", "deuxième", "troisième", "quatrième", "cinquième", "sixième",
+            "septième", "huitième", "neuvième", "dixième", "onzième", "douzième",
+            "treizième", "quatorzième", "quinzième", "seizième",
+            "dix-septième", "dix-huitième", "dix-neuvième"
+        };
+
+        private static string[] OrdinalTenths = {
+            "", "dixième", "vingtième", "trentième", "quarantième", "cinquantième",
+            "soixantième", "soixante-dixième", "quatre-vingtième", "quatre-vingt-dixième"
+        };
+
+        private static string[] OrdinalTenthsPlus = {
+            "", "", "vingt", "trente", "quarante", "cinquante",
+            "soixante", "", "quatre-vingt", ""
+        };
+
+        public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+        {
+            if (numFmt == "cardinalText")
+            {
+                string result = "";
+
+                var sLevel = (levelNumber + 10000).ToString();
+                int thousands = int.Parse(sLevel.Substring(1, 1));
+                int hundreds = int.Parse(sLevel.Substring(2, 1));
+                int tens = int.Parse(sLevel.Substring(3, 1));
+                int ones = int.Parse(sLevel.Substring(4, 1));
+
+                /* exact thousands */
+                if (levelNumber == 1000)
+                    return "Mille";
+                if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0)
+                {
+                    result = OneThroughNineteen[thousands] + " mille";
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* > 1000 */
+                if (levelNumber > 1000 && levelNumber < 2000)
+                    result = "mille ";
+                else if (levelNumber > 2000 && levelNumber < 10000)
+                    result = OneThroughNineteen[thousands] + " mille ";
+
+                /* exact hundreds */
+                if (hundreds > 0 && tens == 0 && ones == 0)
+                {
+                    if (hundreds == 1)
+                        result += "cent";
+                    else
+                        result += OneThroughNineteen[hundreds] + " cents";
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* > 100 */
+                if (hundreds > 0)
+                {
+                    if (hundreds == 1)
+                        result += "cent ";
+                    else
+                        result += OneThroughNineteen[hundreds] + " cent ";
+                }
+
+                /* exact tens */
+                if (tens > 0 && ones == 0)
+                {
+                    if (tens == 8)
+                        result += "quatre-vingts";
+                    else
+                        result += Tens[tens];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* 71-79 */
+                if (tens == 7)
+                {
+                    if (ones == 1)
+                        result += Tens[6] + " et " + OneThroughNineteen[ones + 10];
+                    else
+                        result += Tens[6] + "-" + OneThroughNineteen[ones + 10];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* 91-99 */
+                if (tens == 9)
+                {
+                    result += Tens[8] + "-" + OneThroughNineteen[ones + 10];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                if (tens >= 2)
+                {
+                    if (ones == 1 && tens != 8 && tens != 9)
+                        result += Tens[tens] + " et un";
+                    else
+                        result += Tens[tens] + "-" + OneThroughNineteen[ones];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                if (levelNumber < 20)
+                {
+                    result += OneThroughNineteen[tens * 10 + ones];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                result += OneThroughNineteen[tens * 10 + ones];
+                return result.Substring(0, 1).ToUpper() + result.Substring(1);
+            }
+            if (numFmt == "ordinal")
+            {
+                string suffix;
+                if (levelNumber == 1)
+                    suffix = "er";
+                else
+                    suffix = "e";
+                return levelNumber.ToString() + suffix;
+            }
+            if (numFmt == "ordinalText")
+            {
+                string result = "";
+
+                if (levelNumber == 1)
+                    return "Premier";
+
+                var sLevel = (levelNumber + 10000).ToString();
+                int thousands = int.Parse(sLevel.Substring(1, 1));
+                int hundreds = int.Parse(sLevel.Substring(2, 1));
+                int tens = int.Parse(sLevel.Substring(3, 1));
+                int ones = int.Parse(sLevel.Substring(4, 1));
+
+                /* exact thousands */
+                if (levelNumber == 1000)
+                    return "Millième";
+                if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0)
+                {
+                    result = OneThroughNineteen[thousands] + " millième";
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* > 1000 */
+                if (levelNumber > 1000 && levelNumber < 2000)
+                    result = "mille ";
+                else if (levelNumber > 2000 && levelNumber < 10000)
+                    result = OneThroughNineteen[thousands] + " mille ";
+
+                /* exact hundreds */
+                if (hundreds > 0 && tens == 0 && ones == 0)
+                {
+                    if (hundreds == 1)
+                        result += "centième";
+                    else
+                        result += OneThroughNineteen[hundreds] + " centième";
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* > 100 */
+                if (hundreds > 0)
+                {
+                    if (hundreds == 1)
+                        result += "cent ";
+                    else
+                        result += OneThroughNineteen[hundreds] + " cent ";
+                }
+
+                /* exact tens */
+                if (tens > 0 && ones == 0)
+                {
+                    result += OrdinalTenths[tens];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* 71-79 */
+                if (tens == 7)
+                {
+                    if (ones == 1)
+                        result += OrdinalTenthsPlus[6] + " et " + OrdinalOneThroughNineteen[ones + 10];
+                    else
+                        result += OrdinalTenthsPlus[6] + "-" + OrdinalOneThroughNineteen[ones + 10];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                /* 91-99 */
+                if (tens == 9)
+                {
+                    result += OrdinalTenthsPlus[8] + "-" + OrdinalOneThroughNineteen[ones + 10];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                if (tens >= 2)
+                {
+                    if (ones == 1 && tens != 8 && tens != 9)
+                        result += OrdinalTenthsPlus[tens] + " et unième";
+                    else
+                        result += OrdinalTenthsPlus[tens] + "-" + OrdinalOneThroughNineteen[ones];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                if (levelNumber < 20)
+                {
+                    result += OrdinalOneThroughNineteen[tens * 10 + ones];
+                    return result.Substring(0, 1).ToUpper() + result.Substring(1);
+                }
+
+                result += OrdinalOneThroughNineteen[tens * 10 + ones];
+                return result.Substring(0, 1).ToUpper() + result.Substring(1);
+            }
+            return null;
+        }
+    }
+}

+ 122 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_ru_RU.cs

@@ -0,0 +1,122 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    public class ListItemTextGetter_ru_RU
+    {
+        private static string[] OneThroughNineteen = {
+            "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь",
+            "девять", "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать",
+            "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"
+        };
+
+        private static string[] Tens = {
+            "десять", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят",
+            "восемьдесят", "девяносто"
+        };
+
+        private static string[] OrdinalOneThroughNineteen = {
+            "первый", "второй", "третий", "четвертый", "пятый", "шестой",
+            "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двенадцатый",
+            "тринадцатый", "четырнадцатый", "пятнадцатый", "шестнадцатый",
+            "семнадцатый", "восемнадцатый", "девятнадцатый"
+        };
+
+        private static string[] OrdinalTenths = {
+            "десятый", "двадцатый", "тридцатый", "сороковой", "пятидесятый",
+            "шестидесятый", "семидесятый", "восьмидесятый", "девяностый"
+        };
+
+        // TODO this is not correct for values above 99
+
+        public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+        {
+            if (numFmt == "cardinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1)
+                    result += OneThroughNineteen[t1 - 1] + " thousand";
+                if (t1 >= 1 && t2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (t1 >= 1)
+                    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1)
+                    result += OneThroughNineteen[h1 - 1] + " hundred";
+                if (h1 >= 1 && h2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (h1 >= 1)
+                    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    result += Tens[x - 1];
+                    if (r >= 1)
+                        result += "-" + OneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            if (numFmt == "ordinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1 && t2 != 0)
+                    result += OneThroughNineteen[t1 - 1] + " thousand";
+                if (t1 >= 1 && t2 == 0)
+                {
+                    result += OneThroughNineteen[t1 - 1] + " thousandth";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                if (t1 >= 1)
+                    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1 && h2 != 0)
+                    result += OneThroughNineteen[h1 - 1] + " hundred";
+                if (h1 >= 1 && h2 == 0)
+                {
+                    result += OneThroughNineteen[h1 - 1] + " hundredth";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                if (h1 >= 1)
+                    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OrdinalOneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    if (r == 0)
+                        result += OrdinalTenths[x - 1];
+                    else
+                        result += Tens[x - 1];
+                    if (r >= 1)
+                        result += " " + OrdinalOneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            return null;
+        }
+    }
+}

+ 213 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_sv_SE.cs

@@ -0,0 +1,213 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+	public class ListItemTextGetter_sv_SE
+	{
+		private static string[] OneThroughNineteen = {
+            "", "ett", "två", "tre", "fyra", "fem", "sex", "sju", "åtta",
+            "nio", "tio", "elva", "tolv", "tretton", "fjorton",
+            "femton", "sexton", "sjutton", "arton", "nitton"
+        };
+
+		private static string[] Tens = {
+            "","tio", "tjugo", "trettio", "fyrtio", "femtio", "sextio", "sjuttio", "åttio",
+            "nittio", "etthundra"
+        };
+
+		private static string[] OrdinalOneThroughNineteen = {
+            "", "första", "andra", "tredje", "fjärde", "femte", "sjätte", "sjunde",
+            "åttonde", "nionde", "tionde", "elfte", "tolfte", "trettonde",
+            "fjortonde", "femtonde", "sextonde", "sjuttonde",
+            "artonde", "nittonde"
+        };
+
+		public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+		{
+			switch (numFmt)
+			{
+				case "cardinalText":
+					return NumberAsCardinalText(languageCultureName, levelNumber, numFmt);
+				case "ordinalText":
+					return NumberAsOrdinalText(languageCultureName, levelNumber, numFmt);
+				case "ordinal":
+					return NumberAsOrdinal(languageCultureName, levelNumber, numFmt);
+				default:
+					return null;
+			}
+		}
+		private static string NumberAsCardinalText(string languageCultureName, int levelNumber, string numFmt)
+		{
+			string result = "";
+
+			var sLevel = (levelNumber + 10000).ToString();
+			int thousands = int.Parse(sLevel.Substring(1, 1));
+			int hundreds = int.Parse(sLevel.Substring(2, 1));
+			int tens = int.Parse(sLevel.Substring(3, 1));
+			int ones = int.Parse(sLevel.Substring(4, 1));
+
+
+			//Validation
+			if (thousands > 19)
+				throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 19 999 is not supported");
+			if (levelNumber == 0)
+				return "Noll";
+			if (levelNumber < 0)
+				throw new ArgumentOutOfRangeException("levelNumber", "Converting a negative levelNumber to ordinal text is not supported");
+
+			/* exact thousands */
+			if (levelNumber == 1000)
+				return "Ettusen";
+			if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0)
+			{
+				result = OneThroughNineteen[thousands] + "tusen";
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 1000 */
+			if (levelNumber > 1000 && levelNumber < 2000)
+				result = "ettusen";
+			else if (levelNumber > 2000 && levelNumber < 10000)
+				result = OneThroughNineteen[thousands] + "tusen";
+
+			/* exact hundreds */
+			if (hundreds > 0 && tens == 0 && ones == 0)
+			{
+				if (hundreds == 1)
+					result += "etthundra";
+				else
+					result += OneThroughNineteen[hundreds] + "hundra";
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 100 */
+			if (hundreds > 0)
+			{
+				if (hundreds == 1)
+					result += "etthundra";
+				else
+					result += OneThroughNineteen[hundreds] + "hundra";
+			}
+
+			/* exact tens */
+			if (tens > 0 && ones == 0)
+			{
+				result += Tens[tens];
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 20 */
+			if (tens == 1)
+			{
+				result += OneThroughNineteen[tens * 10 + ones];
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+			else if (tens > 1)
+			{
+				result += Tens[tens] + OneThroughNineteen[ones]; ;
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+			else
+			{
+				result += OneThroughNineteen[ones];
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+		}
+		private static string NumberAsOrdinalText(string languageCultureName, int levelNumber, string numFmt)
+		{
+			string result = "";
+
+			if (levelNumber <= 0)
+				throw new ArgumentOutOfRangeException("levelNumber", "Converting a zero or negative levelNumber to ordinal text is not supported");
+			if(levelNumber >= 10000)
+				throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 10000 is not supported");
+
+			if (levelNumber == 1)
+				return "Första";
+
+			var sLevel = (levelNumber + 10000).ToString();
+			int thousands = int.Parse(sLevel.Substring(1, 1));
+			int hundreds = int.Parse(sLevel.Substring(2, 1));
+			int tens = int.Parse(sLevel.Substring(3, 1));
+			int ones = int.Parse(sLevel.Substring(4, 1));
+
+
+			/* exact thousands */
+			if (levelNumber == 1000)
+				return "Ettusende";
+			if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0)
+			{
+				result = OneThroughNineteen[thousands] + "tusende";
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 1000 */
+			if (levelNumber > 1000 && levelNumber < 2000)
+				result = "ettusen";
+			else if (levelNumber > 2000 && levelNumber < 10000)
+				result = OneThroughNineteen[thousands] + "tusende";
+
+			/* exact hundreds */
+			if (hundreds > 0 && tens == 0 && ones == 0)
+			{
+				if (hundreds == 1)
+					result += "etthundrade";
+				else
+					result += OneThroughNineteen[hundreds] + "hundrade";
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 100 */
+			if (hundreds > 0)
+			{
+				result += OneThroughNineteen[hundreds] + "hundra";
+			}
+
+			/* exact tens */
+			if (tens > 0 && ones == 0)
+			{
+				result += Tens[tens] + "nde";
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+
+			/* > 20 */
+			if (tens == 1)
+			{
+				result += OrdinalOneThroughNineteen[tens * 10 + ones];
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+			else if (tens > 1)
+			{
+				result += Tens[tens] + OrdinalOneThroughNineteen[ones]; ;
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+			else
+			{
+				result += OrdinalOneThroughNineteen[ones];
+				return result.Substring(0, 1).ToUpper() + result.Substring(1);
+			}
+		}
+		private static string NumberAsOrdinal(string languageCultureName, int levelNumber, string numFmt)
+		{
+			string levelAsString = levelNumber.ToString();
+
+            if (levelAsString == null)
+                return "";
+            if (levelAsString.Trim() == "")
+				return "";
+
+			if(levelAsString.EndsWith("1"))
+				return levelAsString + ":a";
+			else if(levelAsString.EndsWith("2"))
+				return levelAsString + ":a";
+			else
+				return levelAsString + ":e";
+		}
+	}
+}

+ 218 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_tr_TR.cs

@@ -0,0 +1,218 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    public class ListItemTextGetter_tr_TR
+    {
+        private static string[] RomanOnes =
+        {
+            "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
+        };
+
+        private static string[] RomanTens =
+        {
+            "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"
+        };
+
+        private static string[] RomanHundreds =
+        {
+            "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M"
+        };
+
+        private static string[] RomanThousands =
+        {
+            "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM",
+            "MMMMMMMMM", "MMMMMMMMMM"
+        };
+
+        private static string[] OneThroughNineteen = {
+            "bir", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz",
+            "dokuz", "on", "onbir", "oniki", "onüç", "ondört",
+            "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz"
+        };
+
+        private static string[] Tens = {
+            "on", "yirmi", "otuz", "kırk", "elli", "altmış", "yetmiş",
+            "seksen", "doksan"
+        };
+
+        private static string[] OrdinalOneThroughNineteen = {
+            "birinci", "ikinci", "üçüncü", "dördüncü", "beşinci", "altıncı",
+            "yedinci", "sekizinci", "dokuzuncu", "onuncu", "onbirinci", "onikinci",
+            "onüçüncü", "ondördüncü", "onbeşinci", "onaltıncı",
+            "onyedinci", "onsekizinci", "ondokuzuncu"
+        };
+
+        private static string[] TwoThroughNineteen = {
+            "", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz",
+            "dokuz", "on", "onbir", "oniki", "onüç", "ondört",
+            "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz"
+        };
+
+        private static string[] OrdinalTenths = {
+            "onuncu", "yirminci", "otuzuncu", "kırkıncı", "ellinci",
+            "altmışıncı", "yetmişinci", "sekseninci", "doksanıncı"
+        };
+
+        public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+        {
+            #region
+            if (numFmt == "decimal")
+            {
+                return levelNumber.ToString();
+            }
+            if (numFmt == "decimalZero")
+            {
+                if (levelNumber <= 9)
+                    return "0" + levelNumber.ToString();
+                else
+                    return levelNumber.ToString();
+            }
+            if (numFmt == "upperRoman")
+            {
+                int ones = levelNumber % 10;
+                int tens = (levelNumber % 100) / 10;
+                int hundreds = (levelNumber % 1000) / 100;
+                int thousands = levelNumber / 1000;
+                return RomanThousands[thousands] + RomanHundreds[hundreds] +
+                    RomanTens[tens] + RomanOnes[ones];
+            }
+            if (numFmt == "lowerRoman")
+            {
+                int ones = levelNumber % 10;
+                int tens = (levelNumber % 100) / 10;
+                int hundreds = (levelNumber % 1000) / 100;
+                int thousands = levelNumber / 1000;
+                return (RomanThousands[thousands] + RomanHundreds[hundreds] +
+                    RomanTens[tens] + RomanOnes[ones]).ToLower();
+            }
+            if (numFmt == "upperLetter")
+            {
+                string a = "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ";
+                //string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+                int c = (levelNumber - 1) / 29;
+                int n = (levelNumber - 1) % 29;
+                char x = a[n];
+                return "".PadRight(c + 1, x);
+            }
+            if (numFmt == "lowerLetter")
+            {
+                string a = "abcçdefgğhıijklmnoöprsştuüvyz";
+                int c = (levelNumber - 1) / 29;
+                int n = (levelNumber - 1) % 29;
+                char x = a[n];
+                return "".PadRight(c + 1, x);
+            }
+            if (numFmt == "ordinal")
+            {
+                string suffix;
+                /*if (levelNumber % 100 == 11 || levelNumber % 100 == 12 ||
+                    levelNumber % 100 == 13)
+                    suffix = "th";
+                else if (levelNumber % 10 == 1)
+                    suffix = "st";
+                else if (levelNumber % 10 == 2)
+                    suffix = "nd";
+                else if (levelNumber % 10 == 3)
+                    suffix = "rd";
+                else
+                    suffix = "th";*/
+                suffix = ".";
+                return levelNumber.ToString() + suffix;
+            }
+            if (numFmt == "cardinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1)
+                    result += OneThroughNineteen[t1 - 1] + " yüz";
+                if (t1 >= 1 && t2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (t1 >= 1)
+                    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1)
+                    result += OneThroughNineteen[h1 - 1] + " bin";
+                if (h1 >= 1 && h2 == 0)
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                if (h1 >= 1)
+                    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    result += Tens[x - 1];
+                    if (r >= 1)
+                        result += /*"-" + */OneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            #endregion
+            if (numFmt == "ordinalText")
+            {
+                string result = "";
+                int t1 = levelNumber / 1000;
+                int t2 = levelNumber % 1000;
+                if (t1 >= 1 && t2 != 0)
+                    result += TwoThroughNineteen[t1 - 1] + "bin";
+                if (t1 >= 1 && t2 == 0)
+                {
+                    result += TwoThroughNineteen[t1 - 1] + "bininci";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                //if (t1 >= 1)
+                //    result += " ";
+                int h1 = (levelNumber % 1000) / 100;
+                int h2 = levelNumber % 100;
+                if (h1 >= 1 && h2 != 0)
+                    result += TwoThroughNineteen[h1 - 1] + "yüz";
+                if (h1 >= 1 && h2 == 0)
+                {
+                    result += TwoThroughNineteen[h1 - 1] + "yüzüncü";
+                    return result.Substring(0, 1).ToUpper() +
+                        result.Substring(1);
+                }
+                //if (h1 >= 1)
+                //    result += " ";
+                int z = levelNumber % 100;
+                if (z <= 19)
+                    result += OrdinalOneThroughNineteen[z - 1];
+                else
+                {
+                    int x = z / 10;
+                    int r = z % 10;
+                    if (r == 0)
+                        result += OrdinalTenths[x - 1];
+                    else
+                        result += Tens[x - 1];
+                    if (r >= 1)
+                        result += OrdinalOneThroughNineteen[r - 1]; //result += "-" + OrdinalOneThroughNineteen[r - 1];
+                }
+                return result.Substring(0, 1).ToUpper() +
+                    result.Substring(1);
+            }
+            if (numFmt == "0001, 0002, 0003, ...")
+            {
+                return string.Format("{0:0000}", levelNumber);
+            }
+            if (numFmt == "bullet")
+                return "";
+            return levelNumber.ToString();
+        }
+    }
+}

+ 130 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/GetListItemText_zh_CN.cs

@@ -0,0 +1,130 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenXmlPowerTools
+{
+    public class ListItemTextGetter_zh_CN
+    {
+        public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt)
+        {
+            string[] ccTDigitCharacters = new[] {
+                "",
+                "一",
+                "二",
+                "三",
+                "四",
+                "五",
+                "六",
+                "七",
+                "八",
+                "九",
+            };
+            string tenCharacter = "十";
+            string hundredCharacter = "百";
+            string thousandCharacter = "千";
+            string andCharacter = "〇";
+
+            string[] ccDigitCharacters = new[] {
+                "○",
+                "一",
+                "二",
+                "三",
+                "四",
+                "五",
+                "六",
+                "七",
+                "八",
+                "九",
+            };
+
+            int thousandsRemainder = levelNumber % 1000;
+            int hundredsRemainder = levelNumber % 100;
+            int thousands = levelNumber / 1000;
+            int hundreds = (levelNumber % 1000) / 100;
+            int tens = (levelNumber % 100) / 10;
+            int ones = levelNumber % 10;
+
+            if (numFmt == "chineseCounting")
+            {
+                if (levelNumber >= 1 && levelNumber <= 9)
+                    return ccDigitCharacters[levelNumber];
+                if (levelNumber >= 10 && levelNumber <= 19)
+                {
+                    if (levelNumber == 10)
+                        return tenCharacter;
+                    return tenCharacter + ccDigitCharacters[ones];
+                }
+                if (levelNumber >= 11 && levelNumber <= 99)
+                {
+                    if (ones == 0)
+                        return ccDigitCharacters[tens] + tenCharacter;
+                    return ccDigitCharacters[tens] + tenCharacter + ccDigitCharacters[ones];
+                }
+                if (levelNumber >= 100 && levelNumber <= 999)
+                    return ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones];
+                if (levelNumber >= 1000 && levelNumber <= 9999)
+                    return ccDigitCharacters[thousands] + ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones];
+                return levelNumber.ToString();
+            }
+            if (numFmt == "chineseCountingThousand")
+            {
+                if (levelNumber >= 1 && levelNumber <= 9)
+                    return ccTDigitCharacters[levelNumber];
+                if (levelNumber >= 10 && levelNumber <= 19)
+                    return tenCharacter + ccTDigitCharacters[ones];
+                if (levelNumber >= 20 && levelNumber <= 99)
+                    return ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones];
+                if (levelNumber >= 100 && levelNumber <= 999)
+                {
+                    if (hundredsRemainder == 0)
+                        return ccTDigitCharacters[hundreds] + hundredCharacter;
+                    if (hundredsRemainder >= 1 && hundredsRemainder <= 9)
+                        return ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[levelNumber % 10];
+                    if (ones == 0)
+                        return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter;
+                    return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones];
+                }
+                if (levelNumber >= 1000 && levelNumber <= 9999)
+                {
+                    if (thousandsRemainder == 0)
+                        return ccTDigitCharacters[thousands] + thousandCharacter;
+                    if (thousandsRemainder >= 1 && thousandsRemainder <= 9)
+                        return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + GetListItemText("zh_CN", thousandsRemainder, numFmt);
+                    if (thousandsRemainder >= 10 && thousandsRemainder <= 99)
+                        return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones];
+                    if (hundredsRemainder == 0)
+                        return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter;
+                    if (hundredsRemainder >= 1 && hundredsRemainder <= 9)
+                        return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[ones];
+                    return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones];
+                }
+                return levelNumber.ToString();
+            }
+            if (numFmt == "ideographTraditional")
+            {
+                string[] iDigitCharacters = new[] {
+                    " ",
+                    "甲",
+                    "乙",
+                    "丙",
+                    "丁",
+                    "戊",
+                    "己",
+                    "庚",
+                    "辛",
+                    "壬",
+                    "癸",
+                };
+                if (levelNumber >= 1 && levelNumber <= 10)
+                    return iDigitCharacters[levelNumber];
+                return levelNumber.ToString();
+            }
+            return null;
+        }
+    }
+}

+ 533 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/HtmlToWmlConverter.cs

@@ -0,0 +1,533 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using DocumentFormat.OpenXml.Packaging;
+using OpenXmlPowerTools;
+using OpenXmlPowerTools.HtmlToWml;
+using OpenXmlPowerTools.HtmlToWml.CSS;
+using System.Text.RegularExpressions;
+
+namespace OpenXmlPowerTools
+{
+    public class HtmlToWmlConverterSettings
+    {
+        public string MajorLatinFont;
+        public string MinorLatinFont;
+        public double DefaultFontSize;
+        public XElement DefaultSpacingElement;
+        public XElement DefaultSpacingElementForParagraphsInTables;
+        public XElement SectPr;
+        public string DefaultBlockContentMargin;
+        public string BaseUriForImages;
+
+        public Twip PageWidthTwips { get { return (long)SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); } }
+        public Twip PageMarginLeftTwips { get { return (long)SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); } }
+        public Twip PageMarginRightTwips { get { return (long)SectPr.Elements(W.pgMar).Attributes(W.right).FirstOrDefault(); } }
+        public Emu PageWidthEmus { get { return Emu.TwipsToEmus(PageWidthTwips); } }
+        public Emu PageMarginLeftEmus { get { return Emu.TwipsToEmus(PageMarginLeftTwips); } }
+        public Emu PageMarginRightEmus { get { return Emu.TwipsToEmus(PageMarginRightTwips); } }
+    }
+
+    public class HtmlToWmlConverter
+    {
+        public static WmlDocument ConvertHtmlToWml(
+            string defaultCss,
+            string authorCss,
+            string userCss,
+            XElement xhtml,
+            HtmlToWmlConverterSettings settings)
+        {
+            return HtmlToWmlConverterCore.ConvertHtmlToWml(defaultCss, authorCss, userCss, xhtml, settings, null, null);
+        }
+
+        public static WmlDocument ConvertHtmlToWml(
+            string defaultCss,
+            string authorCss,
+            string userCss,
+            XElement xhtml,
+            HtmlToWmlConverterSettings settings,
+            WmlDocument emptyDocument,
+            string annotatedHtmlDumpFileName)
+        {
+            return HtmlToWmlConverterCore.ConvertHtmlToWml(defaultCss, authorCss, userCss, xhtml, settings, emptyDocument, annotatedHtmlDumpFileName);
+        }
+
+        private static string s_Blank_wml_base64 = @"UEsDBBQABgAIAAAAIQAJJIeCgQEAAI4FAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAAC
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0
+lE1Pg0AQhu8m/geyVwPbejDGlPag9ahNrPG8LkPZyH5kZ/v17x1KS6qhpVq9kMAy7/vMCzOD0UqX
+0QI8KmtS1k96LAIjbabMLGWv08f4lkUYhMlEaQ2kbA3IRsPLi8F07QAjqjaYsiIEd8c5ygK0wMQ6
+MHSSW69FoFs/407IDzEDft3r3XBpTQAT4lBpsOHgAXIxL0M0XtHjmsRDiSy6r1+svFImnCuVFIFI
++cJk31zirUNClZt3sFAOrwiD8VaH6uSwwbbumaLxKoNoInx4Epow+NL6jGdWzjX1kByXaeG0ea4k
+NPWVmvNWAiJlrsukOdFCmR3/QQ4M6xLw7ylq3RPt31QoxnkOkj52dx4a46rppLbYq+12gxAopFNM
+vv6CcVfouFXuRFjC+8u/UeyJd4LkNBpT8V7CCYn/MIxGuhMi0LwD31z7Z3NsZI5Z0mRMvHVI+8P/
+ou3dgqiqYxo5Bz4oaFZE24g1jrR7zu4Pqu2WQdbizTfbdPgJAAD//wMAUEsDBBQABgAIAAAAIQAe
+kRq38wAAAE4CAAALAAgCX3JlbHMvLnJlbHMgogQCKKAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjJLbSgNBDIbvBd9hyH032woi0tneSKF3
+IusDhJnsAXcOzKTavr2jILpQ217m9OfLT9abg5vUO6c8Bq9hWdWg2JtgR99reG23iwdQWchbmoJn
+DUfOsGlub9YvPJGUoTyMMaui4rOGQSQ+ImYzsKNchci+VLqQHEkJU4+RzBv1jKu6vsf0VwOamaba
+WQ1pZ+9AtcdYNl/WDl03Gn4KZu/Yy4kVyAdhb9kuYipsScZyjWop9SwabDDPJZ2RYqwKNuBpotX1
+RP9fi46FLAmhCYnP83x1nANaXg902aJ5x687HyFZLBZ9e/tDg7MvaD4BAAD//wMAUEsDBBQABgAI
+AAAAIQB8O5c5IgEAALkDAAAcAAgBd29yZC9fcmVscy9kb2N1bWVudC54bWwucmVscyCiBAEooAAB
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKyTTU+EMBCG7yb+B9K7FFZdjdmyFzXZq67x3C1T
+aISWdMYP/r0VswrKogcuTWaavs/TSbtav9VV9AIejbOCpXHCIrDK5cYWgj1sb08uWYQkbS4rZ0Gw
+FpCts+Oj1R1UksIhLE2DUUixKFhJ1FxxjqqEWmLsGrBhRztfSwqlL3gj1ZMsgC+SZMl9P4Nlg8xo
+kwvmN/kpi7ZtE8h/ZzutjYJrp55rsDSC4AhE4WYYMqUvgATbd+Lgyfi4wuKAQm2Ud+g0xcrV/JP+
+Qb0YXowjtRXgo6HyRmtQ1Mf/3JrySA94jIz5H6PoyL1BdPUUfjknnsILgW96V/JuTacczud00M7S
+Vu6qnsdXa0ribE6JV9jd/3qVveZehA8+XPYOAAD//wMAUEsDBBQABgAIAACqpkDt0tkFcQIAAOsF
+AAARAAAAd29yZC9kb2N1bWVudC54bWzsvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN
+5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCq
+yB8/fnwfPyL+x7/3H3z8e7xblOllXjdFtfzso93xzkdpvpxWs2J58dlHX715tn3wUdq02XKWldUy
+/+yj67z56Pc4+o2Tx1ePZtV0vciXbUogls2jq9X0s4/mbbt6dPduM53ni6wZL4ppXTXVeTueVou7
+1fl5Mc3vXlX17O7ezu4O/7aqq2neNNTfSba8zJqPFNyiD61a5Uv68ryqF1lLf9YXdxdZ/Xa92ibo
+q6wtJkVZtNcEe+dTA6b67KN1vXykILYtQnjlkSCkP8wb9W36lVeeKgW4x7t1XhIO1bKZFys3jK8L
+jb6cGyCXmwZxuShNu6vV7v6HzcHTOruiHw7gbdCfyUuLUjDfDHF35xYzAhD2jdugEPZpMFlkxdJ1
+/LVI4xP34usAcFh9XlfrlYNWfBi0s+VbCwuC+R6wdI78oTXvBaCHzOt5tiIBWkwfnV0sqzqblIQR
+UTwFR35E6iJNSWFMqtk1/85/rdKrR6R2Zq8++2hn59793f1j0j360dP8PFuXrfeNvqdwqrcQ/Ndt
+Vrf0SjGjhnh3mS2o39//8+pJNn370d3YO6fLmX3DNHh8l7BxiDX5tH1Z+y+vLl7/gN4iTtzd29vn
+rub0+/2DfQdEG36R1fRtW5Hg7O5L07q4mLfuz0nVttXC/V3m59638zyb5aSCHuzxn+dV1Xp/Xqxb
+/jPE3UMYfyqZ8atR0Uf/TwAAAP//UEsDBBQABgAIAAAAIQAw3UMpqAYAAKQbAAAVAAAAd29yZC90
+aGVtZS90aGVtZTEueG1s7FlPb9s2FL8P2HcgdG9jJ3YaB3WK2LGbLU0bxG6HHmmJlthQokDSSX0b
+2uOAAcO6YYcV2G2HYVuBFtil+zTZOmwd0K+wR1KSxVhekjbYiq0+JBL54/v/Hh+pq9fuxwwdEiEp
+T9pe/XLNQyTxeUCTsO3dHvYvrXlIKpwEmPGEtL0pkd61jfffu4rXVURigmB9Itdx24uUSteXlqQP
+w1he5ilJYG7MRYwVvIpwKRD4COjGbGm5VltdijFNPJTgGMjeGo+pT9BQk/Q2cuI9Bq+JknrAZ2Kg
+SRNnhcEGB3WNkFPZZQIdYtb2gE/Aj4bkvvIQw1LBRNurmZ+3tHF1Ca9ni5hasLa0rm9+2bpsQXCw
+bHiKcFQwrfcbrStbBX0DYGoe1+v1ur16Qc8AsO+DplaWMs1Gf63eyWmWQPZxnna31qw1XHyJ/sqc
+zK1Op9NsZbJYogZkHxtz+LXaamNz2cEbkMU35/CNzma3u+rgDcjiV+fw/Sut1YaLN6CI0eRgDq0d
+2u9n1AvImLPtSvgawNdqGXyGgmgookuzGPNELYq1GN/jog8ADWRY0QSpaUrG2Ico7uJ4JCjWDPA6
+waUZO+TLuSHNC0lf0FS1vQ9TDBkxo/fq+fevnj9Fxw+eHT/46fjhw+MHP1pCzqptnITlVS+//ezP
+xx+jP55+8/LRF9V4Wcb/+sMnv/z8eTUQ0mcmzosvn/z27MmLrz79/btHFfBNgUdl+JDGRKKb5Ajt
+8xgUM1ZxJScjcb4VwwjT8orNJJQ4wZpLBf2eihz0zSlmmXccOTrEteAdAeWjCnh9cs8ReBCJiaIV
+nHei2AHucs46XFRaYUfzKpl5OEnCauZiUsbtY3xYxbuLE8e/vUkKdTMPS0fxbkQcMfcYThQOSUIU
+0nP8gJAK7e5S6th1l/qCSz5W6C5FHUwrTTKkIyeaZou2aQx+mVbpDP52bLN7B3U4q9J6ixy6SMgK
+zCqEHxLmmPE6nigcV5Ec4piVDX4Dq6hKyMFU+GVcTyrwdEgYR72ASFm15pYAfUtO38FQsSrdvsum
+sYsUih5U0byBOS8jt/hBN8JxWoUd0CQqYz+QBxCiGO1xVQXf5W6G6HfwA04WuvsOJY67T68Gt2no
+iDQLED0zERW+vE64E7+DKRtjYkoNFHWnVsc0+bvCzShUbsvh4go3lMoXXz+ukPttLdmbsHtV5cz2
+iUK9CHeyPHe5COjbX5238CTZI5AQ81vUu+L8rjh7//nivCifL74kz6owFGjdi9hG27Td8cKue0wZ
+G6gpIzekabwl7D1BHwb1OnPiJMUpLI3gUWcyMHBwocBmDRJcfURVNIhwCk173dNEQpmRDiVKuYTD
+ohmupK3x0Pgre9Rs6kOIrRwSq10e2OEVPZyfNQoyRqrQHGhzRiuawFmZrVzJiIJur8OsroU6M7e6
+Ec0URYdbobI2sTmUg8kL1WCwsCY0NQhaIbDyKpz5NWs47GBGAm1366PcLcYLF+kiGeGAZD7Ses/7
+qG6clMfKnCJaDxsM+uB4itVK3Fqa7BtwO4uTyuwaC9jl3nsTL+URPPMSUDuZjiwpJydL0FHbazWX
+mx7ycdr2xnBOhsc4Ba9L3UdiFsJlk6+EDftTk9lk+cybrVwxNwnqcPVh7T6nsFMHUiHVFpaRDQ0z
+lYUASzQnK/9yE8x6UQpUVKOzSbGyBsHwr0kBdnRdS8Zj4quys0sj2nb2NSulfKKIGETBERqxidjH
+4H4dqqBPQCVcd5iKoF/gbk5b20y5xTlLuvKNmMHZcczSCGflVqdonskWbgpSIYN5K4kHulXKbpQ7
+vyom5S9IlXIY/89U0fsJ3D6sBNoDPlwNC4x0prQ9LlTEoQqlEfX7AhoHUzsgWuB+F6YhqOCC2vwX
+5FD/tzlnaZi0hkOk2qchEhT2IxUJQvagLJnoO4VYPdu7LEmWETIRVRJXplbsETkkbKhr4Kre2z0U
+QaibapKVAYM7GX/ue5ZBo1A3OeV8cypZsffaHPinOx+bzKCUW4dNQ5PbvxCxaA9mu6pdb5bne29Z
+ET0xa7MaeVYAs9JW0MrS/jVFOOdWayvWnMbLzVw48OK8xjBYNEQp3CEh/Qf2Pyp8Zr926A11yPeh
+tiL4eKGJQdhAVF+yjQfSBdIOjqBxsoM2mDQpa9qsddJWyzfrC+50C74njK0lO4u/z2nsojlz2Tm5
+eJHGzizs2NqOLTQ1ePZkisLQOD/IGMeYz2TlL1l8dA8cvQXfDCZMSRNM8J1KYOihByYPIPktR7N0
+4y8AAAD//wMAUEsDBBQABgAIAKSopkCDd7GUMQQAAEIKAAARAAAAd29yZC9zZXR0aW5ncy54bWzs
+vQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz3
+3nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyL+x7/3H3z8e7xblOllXjdF
+tfzso93xzkdpvpxWs2J58dlHX715tn3wUdq02XKWldUy/+yj67z56Pc4+o2Tx1ePmrxtqVmTEohl
+82gx/eyjeduuHt2920zn+SJrxtUqX9KX51W9yFr6s764u8jqt+vV9rRarLK2mBRl0V7f3dvZ+fQj
+BVN99tG6Xj5SENuLYlpXTXXe4pVH1fl5Mc31h3mjvk2/8srTarpe5MuWe7xb5yXhUC2bebFqDLTF
+14VGX84NkMtNg7hclKbd1e7OLYZ7VdUz+8Zt0MMLq7qa5k1DE7QoDYLF0nW83wNk+x5T3zpEBkWv
+7+7wbw7zprwNIvLV82JSZ/W1j8Vi+ujsYlnV2aQkpiJsPiKeSlPiqh9U1SK9erTK6ymRllhyZ+ej
+u+ZLGlR1/rrN2pyaNKu8LJlPp2WeEdCrRxd1tiAOM5/Y92b5ebYu2zfZ5HVbrajhZUb4P9jzQE/n
+WZ1N27x+vcqmBPWkWrZ1VZq2s+pF1Z4Q19ZEVO8t5mP+y/v7tUgGvbvMFjS+gNu/qGY5cF3Xxe2n
+4CODB1Hq7k3dVSTPdTHL34C6r9vrMn9Gg3ld/CA/Xs6+s27aguAy538AHjejkS/R/5fEFW+uV/mz
+PGvXRLyf1S55lp6VxeqLoq6r+mw5Ix76Brp8fDeYauqfFOascejgz1dV1ZoXd3buH+/cPzn1UUYb
+9/3e03v3ngRD6n1//+GG7+/d390/Nvwb+X7/2YPT3QfD39+E36f37x0/+3T4+ycP7h3fPx7+/nT/
+3sHxBvyf7eye7j706Oso+njxCMr0ZW3elb/BxOlC3j/JFpO6yNIvoHRtH4tHk/rtk2JpWk1yUkZ5
+//vX64lpsr3tf90ssrJ8RnrAfO0RePFoVjSrp/m590n5RVZfuP6C1vWG70gffcf2AT2X15/X1Xrl
+t7mqs5UwsGm4u78fQCmW7fNiYb5t1pPXIYQlKV2vwXo5+/KythQPiEzT05JQsKJ4nrFE8Tv5cvur
+1566K+vXEJ78i2y1EsGbXOx+9lFZXMzbXYhNS3/NyMbzH5OLPf1uj7/bk+/4j2yKcVNr/cV9tmc+
+89rdM5/dc5/tm8/23Wf3zWf33Wefms8+xWdz0kU1mY23pA7Mr/j8vCrL6iqffdt93/vIEaKZZ6v8
+qVgVqwYq+ViNTZNePsrfkQXLZ0VLTtSqmC2ydzBoe55Y6Ttldl2t2+ANbcFt8OoqhDfL2izQiXcD
+UFas+piyPZwWxOqvrxcTZ+LGbnhl0ZBuXZFFbKvafD/i7xmmen5H/08AAAD//1BLAwQUAAYACAAA
+ACEAF6AWTgIBAACsAQAAFAAAAHdvcmQvd2ViU2V0dGluZ3MueG1sjNDBSgMxEAbgu+A7LLm32ZUi
+snS3IFLxIoL6AGl2dhvMZMJMaqxPb9qqIF56yySZj5l/ufpAX70Di6PQqWZeqwqCpcGFqVOvL+vZ
+jaokmTAYTwE6tQdRq/7yYpnbDJtnSKn8lKooQVq0ndqmFFutxW4BjcwpQiiPIzGaVEqeNBp+28WZ
+JYwmuY3zLu31VV1fq2+Gz1FoHJ2FO7I7hJCO/ZrBF5GCbF2UHy2fo2XiITJZECn7oD95aFz4ZZrF
+PwidZRIa07wso08T6QNV2pv6eEKvKrTtwxSIzcaXBHOzUH2Jj2Jy6D5hTXzLlAVYH66N95SfHu9L
+of9k3H8BAAD//wMAUEsDBBQABgAIAAAAIQCAS4U32AgAAAJCAAAaAAAAd29yZC9zdHlsZXNXaXRo
+RWZmZWN0cy54bWzsW0tz2zYQvnem/4HDuyPJcqzEEyXjOHHiGecpe3qGKMhiTRIsH3bcX9/FgoQo
+UhR3TebWk0wQ2G9f+BaSsW/e/QoD50Emqa+iuTt5MXYdGXlq5Ud3c/f25vLoleukmYhWIlCRnLtP
+MnXfvf3zjzePZ2n2FMjUAQFRevYYe3N3k2Xx2WiUehsZivRF6HuJStU6e+GpcKTWa9+To0eVrEbH
+48kY/4oT5ck0BbQLET2I1C3EhU1pKpYRYK1VEoosfaGSu1Eokvs8PgLpscj8pR/42RPIHp+WYtTc
+zZPorFDoyCqkl5wZhYqPckXSsGIPrln5QXl5KKMMEUeJDEAHFaUbP96a8VxpYOKmVOnhkBEPYVDO
+e4wnJw08azIlBh8S8Qih2ApsiNvjjJVZFAbGDzq+26jWJU7Gh4wpIqJFWB0oKuxilpqEwo+smOe5
+pupc2A998vtTovLYqhP7/aRdRfdWlt6WDM3Gp7jzqqalLAGNrbvYiFi6TuidXd1FKhHLADR6nJw4
+OiPdt0AVK+V9kGuRB1mqH5PvSfFYPOHHpYqy1Hk8E6nn+zdAISAl9EHg5/Mo9V14I0Wanae+2Pty
+o2ftfeOlWUXae3/luyONmP4LMh9EMHePj8uRC63BzlggortyTEZHt4uqJnPXDi1B7twVydHiXAsb
+oZnlZ8XceMd4eEJVYuHBzgMcsc4kkBCwmMYJfB3d4xkwmnn4mWvnijxTBQgKALCqWHiseRy4CZhq
+YRgb3sr1tfLu5WqRwYu5i1gweHv1PfFVAjQ6d1+/1pgwuJCh/9lfraQuEMXYbbTxV/KvjYxuU7na
+jv+4RHouJHoqjzJQ/3SGWRCkq4+/PBlrmgTRkdAR/qoXAIdBOCo4qFDub7UxAzVUHPynhJyYGO5F
+2UihS5qD+h8EQqvz3kDH2qKqASiXpeu0v4iT/iJe9heBydvPF7P+WsBBpm9ETG5UspIe1Ex5Jvmq
+fpi+PpCyekUjizpXNJKmc0UjRzpXNFKic0UjAzpXNALeuaIR384VjXAeXOEJJK56Fk3RG6SNfeNn
+AdTJDqab9KS6otQ430Ui7hIRbxxdWOtqHyLLRb7MaKoinT6fLBdZovRxs8MjUJ311n02J38M441I
+fTiVdwH1dP2NPvo4nxIfjq8dUC9N8jVswoPJ3hL2PRCe3KhgJRPnRv4yEWWs/6qchTlldCrXM6zX
+/t0mc+BUqEtuJ9hpi9PbPWHkX/sp+uBgNT9tMaVLOCmGpy152S78i1z5eVi6hnAaOTV8zghzDQJV
+POyiEx2i5u7qtEIHgGKCKRd8E1A+QX9TXPjydYwp+ptS9Ez5BP1N4XqmfMyPw/FlM80H+FnFIW2v
+GXvvXqhAJes8KPdAJz3M2DvYQtBMYG9iK59EEjP2Dt6hT+fc8+CbGyVP2bHY8igDhR0Og4KbjW4L
+Oyg12pswLGIHqIZ1zMDqx7UMIDbp/pQPvv4RmFsMkKXtWbNzO09bPAAliHSG/pGrrPsMfdzCeVSU
+qwh+LkmlQ0Obtuw8KlqRT6beMWLcr/AxgPpVQAZQv1LIAGrJj/Yzj62JdJD+xZGBxaZlW8Uw7cjM
+PGMzswXilYCB6ibh/NWye9tzoVk3CSjsADXrJgGFHZ1aLbN1k4A1WN0kYLVUjfYYVTmVYxS7blaB
+7EmAYNEw5E0AGoa8CUDDkDcBqD95d4MMR94ELDY3WE6tkjcBCKdwvupboCp5E4DY3GDYrvjNqKx7
+KOXwl9sByJuAwg5Qk7wJKOzotJE3AQuncDKhhmWpjoA1DHkTgIYhbwLQMORNABqGvAlAw5A3Aag/
+eXeDDEfeBCw2N1hOrZI3AYhNDxaoSt4EIJzC4Ya95I27/reTNwGFHaAmeRNQ2NGpEao9pBKw2AGq
+YVnyJmDhFE4yFFiY3ByjhiFvgkXDkDcBaBjyJgANQ94EoP7k3Q0yHHkTsNjcYDm1St4EIDY9WKAq
+eROA2Nywl7xxM/528iagsAPUJG8CCjs6NUK1PEfAYgeohmXJm4CF+dKbvAlAOOW5QByLhiFvgkXD
+kDcBaBjyJgD1J+9ukOHIm4DF5gbLqVXyJgCx6cECVcmbAMTmhr3kjXvkt5M3AYUdoCZ5E1DY0akR
+qiVvAhY7QDUsS3UErGHImwCEidmbvAlAOOUZQLiLOGEahrwJFg1D3gSg/uTdDTIceROw2NxgObVK
+3gQgNj1YoCp5E4DY3KDv2cJ9UfL11ElLElDvGZS3GsiAxy1BogIWBv6Ua5lAV6Hsvh3SE7C0kIHY
+kh5UE98rde/QLnZPWxKEDOUvA1/hle4nvKVTaUSYzg50Etx8u3A+mwaYxjpMqd2bN9A9VG0XwvYk
+3TgEemZPMbTsxOXNci0NGoR0X1fRAoQ9oVfQEFS09ejFus8HJmJTVTGM/7ctUPFv6D9dlXPG45PL
+2cdJYVGjQWopoQUUtJiYDinzeA4NUam53VxoUvRRFbPwqTmpaK86wf8i6YfW9io0rMMV1vjC2RPs
+eqqav21DQquXApqnvuleqIZzIrjhvW8clLwvx0uYi41ITPi3zSXlnKLDpN3X72fT85fYsYY9ZNrE
+eynjr4CPOuqHa/BMik8qz7Sbrh+CEmCskU3/mV4LrX34sbeZT/x9oJlPv/xYNPjpxNrp59tZue3n
+08Pbfr6lceqFUdXTN01LLaenLy9fI7lgKyBSPLTR4d3K7bD+9yNk1vtL481Kf+CrcqTSH4hjYDma
+DJ8tKeJBdIQHTX0HdkvRs2Gv0WHHhvZjNXlaGjvQ6GbgiwaP7dcAM2/nmrGJXIvemW5mOKAzNjsc
+3OYOTjGeayoI/YWoUpeGwDrLwGQV/HEVaZ54LBoMDR+tfgkjCt5fyCD4IjAHMxW3Tw3kWu8vEDQZ
+4yGuJmqpskyF7esT7HFoFQDpUFXGPGoj2vMkysOlTIqOiVZW1YefBq1AaweOt6QC1dPtuu3ksJen
+4JqFLgl11t9hpHr+Fi+dibMlrBoD7t0HaNU+3mvNLPNit6ZUee5/kipDnb79DwAA//8DAFBLAwQU
+AAYACAAAACEA7eys33sBAADfAgAAEQAIAWRvY1Byb3BzL2NvcmUueG1sIKIEASigAAEAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAfJJdT8IwFIbvTfwPS+9HuwFGFjYSP7iSxESMxrvaHqCydU1b
+mPx7zzYYLBqTXvR8Peect53Ovos82IN1qtQpiQaMBKBFKZVep+R1OQ9vSeA815LnpYaUHMCRWXZ9
+NRUmEaWFZ1sasF6BC5CkXSJMSjbem4RSJzZQcDfADI3BVWkL7tG0a2q42PI10JixG1qA55J7Tmtg
+aDoiOSKl6JBmZ/MGIAWFHArQ3tFoENFzrgdbuD8LmshFZqH8weBOx3Ev2VK0wS7726kusaqqQTVs
+xsD5I/q+eHppVg2VrrUSQLKpFIlXPodsSs9XvLnd5xcI37o7AwPCAvelzR6tEk3NyVFLvYVDVVrp
+sKxnYZ0EJ6wyHh+whfYcmJ1z5xf4oisF8u5w5P/2120s7FX9E7JJ06czcZtGvHZIkAHKkbTinSJv
+w/uH5ZxkMYuiEA+Ll2yUROOEsY96nV59LU/rKI6D/U+MQzbGs2STZDzqE0+AVpn+l8x+AAAA//8D
+AFBLAwQUAAYACAB1gqVAQzGb3QQJAACCRAAADwAAAHdvcmQvc3R5bGVzLnhtbOy9B2AcSZYlJi9t
+ynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733
+ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/Iv7Hv/cffPx7vFuU6WVeN0W1/Oyj3fHOR2m+
+nFazYnnx2UdfvXm2ffBR2rTZcpaV1TL/7KPrvPno9zj6jZPHV4+a9rrMm5QALJtHi+lnH83bdvXo
+7t1mOs8XWTOuVvmSvjyv6kXW0p/1xd1FVr9dr7an1WKVtcWkKIv2+u7ezs6nHymY+jZQqvPzYpo/
+rabrRb5s+f27dV4SxGrZzItVY6Bd3QbaVVXPVnU1zZuGBr0oBd4iK5YWzO5+D9CimNZVU523YxqM
+YsSg6PXdHf5tUX6ULqaPzi6WVZ1NSiIeAfqIaJemRL1ZNX2an2frsm34I/6wflnrh/qZ+dT+KR88
+q5Ztk149ypppUbwhjAj4oqB+vn28bIqP6Js8a9rjpsiiX87xS/SbadN6Hz8pZsVHd8O+mx9Qs8us
+/Oyjvb3+dyfN8Ldltrww3+bL7a9e+3h6H02o188+yurt18ceiMd3fULoXyGxqI9VlICrLgGbVTYt
+GJvsvM2J6WjO0XVZgMf3Hnxq/ni1xrxl67YKR/O7bm8HYCY5sRS13BU48ucxvaZN6JuPXG/aiv/q
+N1Ik9i1GARLb2wFJ3ND0L3/4+KjLZzwRLcnNaxFfapGfP6+mb/PZ65a++Owj7pc+/OrsZV1UNYno
+Zx89fKgfvs4XxbeL2SxfKrZouJwXs/y783z5VZPP3Oc/8YzFTCFOq/WSft/79MFHbr7KZnb6bpqv
+ILrUZpmB917gtRLvNF5vDGRdOJzkg07f/OEvMh3v2lkb6mueZ9B16e6N3T38Jrvbi0L/GoDufVOA
+9r8pQPe/KUCfflOAHnxTgA6+KUAPPxRQW02FZX0g9x7e6r0e793yvR6r3fK9Hmfd8r0eI93yvR7f
+3PK9Hpvc8r0eV9zyvR4T3OK9acZ/995kWr0H/7wp2jK/UeXtfiMqVs1P+jKrs4s6W81TuC+9vm6E
+83o9aW+H9u43gfbrtq6WFzd2tifi9IGdnS5W86wpmpu7+0am5A380fTzupjd2OH9Abt3Uxcvy2ya
+z6tyltfpm/xd+/WgvKjS1+Ic3YjoNzLpz4uLeZu+nrOavrHLTwcm43a9PC+a9uYuBoZ1uy5uNcOf
+DnDwTV18kc+K9cIQ6xYe1Kf3vpGO9m7uaP+DOsLE3GY49z+8l1uM5dMP6gUcMDQWv5cHH97LLcZy
+8OG93Lu5l6+psZ5ScuJ2Qvnga8r9SVVW9fm6vLWCefA1pd92dLvhfE0FYHu5lZp58DWlP1DJ6fF0
+SrHrbTj6a86R083v0dfXnCanpN+jr685WV1t/R49fs2J66rt9+jxm9Df79Hd11Tkr/LLAnnTr/c2
+Y2l94huRvDdAk/S9/JufWFftzQ703jeS6zhbUpqpydPb9XlvQF7fr8/Atr4HB3wTRvY9uvsmrO17
+dPdNmN336O5D7e/tu/qmDPF79Pg1VX1gkd+ju6+p7QPT/B7dfU1VH7XRt/AHv+b09W30Lfr6mhPX
+t9G36OtrztqQjb5Fj19z4oZs9C16/CZt9C26+5o2OmoQbtHdN2kQbtHdN2kQbtHdN2kQbtHdN2UQ
+bu7qmzYIt+jxa+qVqEG4RXdfU7XEDMJtuvuaeiVqEG4Run/N6esbhFv09TUnrm8QbtHX15y1IYNw
+ix6/5sQNGYRb9PhNGoRbdPdNGoRbdPdNGoRbdPdNGoRbdPdNGoRbdPdNGYSbu/qmDcItevyaeiVq
+EG7R3ddULVGDcIvuvqZeiRqE/Ru7++YMwi36+poT1zcIt+jra87akEG4RY9fc+KGDMItevwmDcIt
+uvsmDcItuvsmDcItuvsmDcItuvsmDcItuvumDMLNXX3TBuEWPX5NvRI1CLfo7muqlqhBuEV3X1Ov
+RA3C/Ru7++YMwi36+poT1zcIt+jra87akEG4RY9fc+KGDMItevwmDcItuvsmDcItuvsmDcItuvtw
+g/Beo/smDcItuvumDMLNXX3TBuEWPX5NvRI1CLfo7muqlqhBuEV3X1OvRA3Cpzd2980ZhFv09TUn
+rm8QbtHX15y1IYNwix6/5sQNGYRb9PhNGoRbdPdNGoRbdPdNGoRbdPdNGoRbdPdNGoRbdPdNGYSb
+u/qmDcItevyaeiVqEG7R3ddULVGDcIvuvqZeeb2etGWeni5W86wpmhv72R1gEfrwFz2r6kXWUpsb
+ez1btvmyeY9u9wYm7/261cG+ys/zOl9O8xu7vfeNdGtG+x79DjDP+/X7pKrepm8KGvLNHQ6wz3t2
+WEzKorqos9X8utfDgxtff/PlSfrtnMW69/bDKCqP7xKojKjbvm6vy7yRD2ms+IteaK9XBHeV1Rkj
+BRiz/Dxblwwj1YZnNNQXgFx+5DAEStTgMivtlx7+ion3Sd2QuGr7nZ39Zw9Od82IgSV3dAv0LEJK
+iN0BlObydRpMyiSjaftyOYz2Mn/XDn9bFsu35lvT/ck8q/02blJMy4fvQ5cnD+4d3z/231i9rPUP
+/vNtnq9eEJamhf3webHMm+DTat0Sxvnzy9LCd4Dv+pCBRthN/axatg29lzXTongzz8F/i+ynq/rb
+x8umwEzkWdMeN0Xmf3mqn+H7ORpG35w2rffxk2JWWLxkmsK/TsJhTWEAzIjufXr/2UPmVAbJxuGz
+jzK2Cu5j2EKI37Own+YHBszeQfebkybyHahmCHUD006JL7Jpm9cbZOqpfPzS8DdoPsDO2jS1bVNu
+vJHxAtZ32s1/J9BntxLGNpuIuhwY0xt8fwtlkXLDj+4ebZKcQHRuPQL+op2UIUfTB2dLSNqV6m0Z
+z+xdFk49tTvJy/KLzL0tn1erG17mZmV+3kq73Z2DjS0nVdtWi9vArDlyuwkoJq6HvH54O55drheT
+vFbrMmgH4OYNTi07gTfow683q+8jb9N1Q6Rlk9fFP9Db8VFoEwp7usq9Yz+i8nuDvXgPW3GTZfiR
+yg6/eS+VbX9vjv6fAAAA//9QSwMEFAAGAAgAAAAhAE229p7CAQAAogQAABIAAAB3b3JkL2ZvbnRU
+YWJsZS54bWykkk1u2zAQhfcFegeB+5ikrKSJEDkI3BroposiPQBNUxZR/ggc2qpv3xEpKwsjgN1K
+ACG94TzMfHjPL3+sKY4qgPauIXzBSKGc9Dvt9g359ba5eyQFROF2wninGnJSQF5Wnz89D3XrXYQC
++x3UVjaki7GvKQXZKStg4XvlsNj6YEXE37CnVoTfh/5OetuLqLfa6HiiJWMPZLIJ17j4ttVSffXy
+YJWLqZ8GZdDRO+h0D2e34Rq3wYddH7xUALizNdnPCu1mG15dGFktgwffxgUuQ/NEdLTCds7SlzWk
+sLL+vnc+iK1BdgOvyGoCVwy1ExbFtTB6G3Qq9MJ5UBxrR2Eawkq2Yfd4jm/FluNJ6OggOxFAxfki
+y3IrrDanswqDBsiFXkfZnfWjCHocKJdA77FwgC1ryDfOGCs3G5IV3pAKhdf1rJQ4VH6epjvLWcHk
+4GDJJ13hT8kHFfSZutKcNEfngsSbtgqKH2oofnor3AdESvaAJO6Rx0hmeRORkHwTwWuJ4ODl67w/
+brJG5ctjxaf9byKSfa4nshYWoyE+IDESyCRGIrdl499IXGaDVTObdxIpCZio/8nGFBJY/QUAAP//
+AwBQSwMEFAAGAAgAAAAhAE5wytZwAQAAxQIAABAACAFkb2NQcm9wcy9hcHAueG1sIKIEASigAAEA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnFLLTsMwELwj8Q9R7tQpQghVW1eoCHHgUamBni17
+k1g4tmWbqv171g1tg7iR086sdzI7Nix2vSm2GKJ2dl5OJ1VZoJVOadvOy/f68equLGISVgnjLM7L
+PcZywS8vYBWcx5A0xoIkbJyXXUp+xliUHfYiTqhtqdO40ItEMLTMNY2W+ODkV482seuqumW4S2gV
+qit/EiwHxdk2/VdUOZn9xY9678kwhxp7b0RC/prtmIlyqQd2YqF2SZha98inRJ8ArESLMXNDARsX
+VOQVsKGAZSeCkInyy+QIwb33RkuRKFf+omVw0TWpeDskUORpYOMjQKmsUX4FnfZZagzhWVtyQexQ
+kKsg2iB8dyBHCNZSGFzS6rwRJiKwMwFL13th95x8HivS+4zvvnYPOZufkd/kaMWNTt3aCzl4OS87
+4mFNgaAi90e1MwFPdBnB5F/SrG1RHc/8beT4PoZXyac3k4q+Q15Hji7k9Fz4NwAAAP//AwBQSwEC
+LQAUAAYACAAAACEACSSHgoEBAACOBQAAEwAAAAAAAAAAAAAAAAAAAAAAW0NvbnRlbnRfVHlwZXNd
+LnhtbFBLAQItABQABgAIAAAAIQAekRq38wAAAE4CAAALAAAAAAAAAAAAAAAAALoDAABfcmVscy8u
+cmVsc1BLAQItABQABgAIAAAAIQB8O5c5IgEAALkDAAAcAAAAAAAAAAAAAAAAAN4GAAB3b3JkL19y
+ZWxzL2RvY3VtZW50LnhtbC5yZWxzUEsBAi0AFAAGAAgA7qmmQO3S2QVxAgAA6wUAABEAAAAAAAAA
+AAAAAAAAQgkAAHdvcmQvZG9jdW1lbnQueG1sUEsBAi0AFAAGAAgAAAAhADDdQymoBgAApBsAABUA
+AAAAAAAAAAAAAAAA4gsAAHdvcmQvdGhlbWUvdGhlbWUxLnhtbFBLAQItABQABgAIAKSopkCDd7GU
+MQQAAEIKAAARAAAAAAAAAAAAAAAAAL0SAAB3b3JkL3NldHRpbmdzLnhtbFBLAQItABQABgAIAAAA
+IQAXoBZOAgEAAKwBAAAUAAAAAAAAAAAAAAAAAB0XAAB3b3JkL3dlYlNldHRpbmdzLnhtbFBLAQIt
+ABQABgAIAAAAIQCAS4U32AgAAAJCAAAaAAAAAAAAAAAAAAAAAFEYAAB3b3JkL3N0eWxlc1dpdGhF
+ZmZlY3RzLnhtbFBLAQItABQABgAIAAAAIQDt7KzfewEAAN8CAAARAAAAAAAAAAAAAAAAAGEhAABk
+b2NQcm9wcy9jb3JlLnhtbFBLAQItABQABgAIAHWCpUBDMZvdBAkAAIJEAAAPAAAAAAAAAAAAAAAA
+ABMkAAB3b3JkL3N0eWxlcy54bWxQSwECLQAUAAYACAAAACEATbb2nsIBAACiBAAAEgAAAAAAAAAA
+AAAAAABELQAAd29yZC9mb250VGFibGUueG1sUEsBAi0AFAAGAAgAAAAhAE5wytZwAQAAxQIAABAA
+AAAAAAAAAAAAAAAANi8AAGRvY1Byb3BzL2FwcC54bWxQSwUGAAAAAAwADAAJAwAA3DEAAAAA";
+
+        private static WmlDocument s_EmptyDocument = null;
+
+        public static WmlDocument EmptyDocument
+        {
+            get {
+                if (s_EmptyDocument == null)
+                {
+                    s_EmptyDocument = new WmlDocument("EmptyDocument.docx", Convert.FromBase64String(s_Blank_wml_base64));
+                }
+                return s_EmptyDocument;
+            }
+        }
+
+        public static HtmlToWmlConverterSettings GetDefaultSettings()
+        {
+            return GetDefaultSettings(EmptyDocument);
+        }
+
+        public static HtmlToWmlConverterSettings GetDefaultSettings(WmlDocument wmlDocument)
+        {
+            HtmlToWmlConverterSettings settings = new HtmlToWmlConverterSettings();
+            using (MemoryStream ms = new MemoryStream())
+            {
+                ms.Write(wmlDocument.DocumentByteArray, 0, wmlDocument.DocumentByteArray.Length);
+                using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false))
+                {
+                    string majorLatinFont, minorLatinFont;
+                    double defaultFontSize;
+                    GetDefaultFontInfo(wDoc, out majorLatinFont, out minorLatinFont, out defaultFontSize);
+                    settings.MajorLatinFont = majorLatinFont;
+                    settings.MinorLatinFont = minorLatinFont;
+                    settings.DefaultFontSize = defaultFontSize;
+
+                    settings.MinorLatinFont = "Times New Roman";
+                    settings.DefaultFontSize = 12d;
+                    settings.DefaultBlockContentMargin = "auto";
+                    settings.DefaultSpacingElement = new XElement(W.spacing,
+                        new XAttribute(W.before, 100),
+                        new XAttribute(W.beforeAutospacing, 1),
+                        new XAttribute(W.after, 100),
+                        new XAttribute(W.afterAutospacing, 1),
+                        new XAttribute(W.line, 240),
+                        new XAttribute(W.lineRule, "auto"));
+                    settings.DefaultSpacingElementForParagraphsInTables = new XElement(W.spacing,
+                        new XAttribute(W.before, 100),
+                        new XAttribute(W.beforeAutospacing, 1),
+                        new XAttribute(W.after, 100),
+                        new XAttribute(W.afterAutospacing, 1),
+                        new XAttribute(W.line, 240),
+                        new XAttribute(W.lineRule, "auto"));
+
+                    XDocument mXDoc = wDoc.MainDocumentPart.GetXDocument();
+                    XElement existingSectPr = mXDoc.Root.Descendants(W.sectPr).FirstOrDefault();
+                    settings.SectPr = new XElement(W.sectPr,
+                        existingSectPr.Elements(W.pgSz),
+                        existingSectPr.Elements(W.pgMar));
+                }
+            }
+            return settings;
+        }
+
+        private static void GetDefaultFontInfo(WordprocessingDocument wDoc, out string majorLatinFont, out string minorLatinFont, out double defaultFontSize)
+        {
+            if (wDoc.MainDocumentPart.ThemePart != null)
+            {
+                XElement fontScheme = wDoc.MainDocumentPart.ThemePart.GetXDocument().Root.Elements(A.themeElements).Elements(A.fontScheme).FirstOrDefault();
+                if (fontScheme != null)
+                {
+                    majorLatinFont = (string)fontScheme.Elements(A.majorFont).Elements(A.latin).Attributes(NoNamespace.typeface).FirstOrDefault();
+                    minorLatinFont = (string)fontScheme.Elements(A.minorFont).Elements(A.latin).Attributes(NoNamespace.typeface).FirstOrDefault();
+                    string defaultFontSizeString = (string)wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.docDefaults)
+                        .Elements(W.rPrDefault).Elements(W.rPr).Elements(W.sz).Attributes(W.val).FirstOrDefault();
+                    if (defaultFontSizeString != null)
+                    {
+                        double dfs;
+                        if (double.TryParse(defaultFontSizeString, out dfs))
+                        {
+                            defaultFontSize = dfs / 2d;
+                            return;
+                        }
+                        defaultFontSize = 12;
+                        return;
+                    }
+                }
+            }
+            majorLatinFont = "";
+            minorLatinFont = "";
+            defaultFontSize = 12;
+        }
+
+        public static string CleanUpCss(string css)
+        {
+            if (css == null)
+                return "";
+            css = css.Trim();
+            string cleanCss = Regex.Split(css, "\r\n|\r|\n")
+                .Where(l =>
+                {
+                    string lTrim = l.Trim();
+                    if (lTrim == "//")
+                        return false;
+                    if (lTrim == "////")
+                        return false;
+                    if (lTrim == "<!--" || lTrim == "&lt;!--")
+                        return false;
+                    if (lTrim == "-->" || lTrim == "--&gt;")
+                        return false;
+                    return true;
+                })
+                .Select(l => l + Environment.NewLine )
+                .StringConcatenate();
+            return cleanCss;
+        }
+    }
+
+    public struct Emu
+    {
+        public long m_Value;
+        public static int s_EmusPerInch = 914400;
+
+        public static Emu TwipsToEmus(long twips)
+        {
+            float v1 = (float)twips / 20f;
+            float v2 = v1 / 72f;
+            float v3 = v2 * s_EmusPerInch;
+            long emus = (long)v3;
+            return new Emu(emus);
+        }
+
+        public static Emu PointsToEmus(double points)
+        {
+            double v1 = points / 72;
+            double v2 = v1 * s_EmusPerInch;
+            long emus = (long)v2;
+            return new Emu(emus);
+        }
+
+        public Emu(long value)
+        {
+            m_Value = value;
+        }
+
+        public static implicit operator long(Emu e)
+        {
+            return e.m_Value;
+        }
+
+        public static implicit operator Emu(long l)
+        {
+            return new Emu(l);
+        }
+
+        public override string ToString()
+        {
+            throw new OpenXmlPowerToolsException("Can't convert directly to string, must cast to long");
+        }
+    }
+
+    public struct TPoint
+    {
+        public double m_Value;
+
+        public TPoint(double value)
+        {
+            m_Value = value;
+        }
+
+        public static implicit operator double(TPoint t)
+        {
+            return t.m_Value;
+        }
+
+        public static implicit operator TPoint(double d)
+        {
+            return new TPoint(d);
+        }
+
+        public override string ToString()
+        {
+            throw new OpenXmlPowerToolsException("Can't convert directly to string, must cast to double");
+        }
+    }
+
+    public struct Twip
+    {
+        public long m_Value;
+
+        public Twip(long value)
+        {
+            m_Value = value;
+        }
+
+        public static implicit operator long(Twip t)
+        {
+            return t.m_Value;
+        }
+
+        public static implicit operator Twip(long l)
+        {
+            return new Twip(l);
+        }
+
+        public static implicit operator Twip(double d)
+        {
+            return new Twip((long)d);
+        }
+
+        public override string ToString()
+        {
+            throw new OpenXmlPowerToolsException("Can't convert directly to string, must cast to long");
+        }
+    }
+
+    public class SizeEmu
+    {
+        public Emu m_Height;
+        public Emu m_Width;
+
+        public SizeEmu(Emu width, Emu height)
+        {
+            m_Width = width;
+            m_Height = height;
+        }
+
+        public SizeEmu(long width, long height)
+        {
+            m_Width = width;
+            m_Height = height;
+        }
+    }
+}
+

+ 0 - 0
TEAMModelOS.SDK/Module/OpenXmlTool/HtmlToWmlConverterCore.cs


Some files were not shown because too many files changed in this diff