Browse Source

增加超链接

LiuQilin 7 năm trước cách đây
mục cha
commit
06e78c60d0
70 tập tin đã thay đổi với 4509 bổ sung38 xóa
  1. 74 34
      Assets/Resource/Prefab/PrefabUI/Canvas.prefab
  2. 1 1
      Assets/Resource/XML/lan/ChineseSimplified.xml
  3. 1 1
      Assets/Resource/XML/lan/ChineseTraditional.xml
  4. 1 1
      Assets/Resource/XML/lan/English.xml
  5. BIN
      Assets/Resource/Xlsx/language_config.xlsx
  6. 3 1
      Assets/Script/Object/Player.cs
  7. 9 0
      Assets/Tookits/TextUtility.meta
  8. 9 0
      Assets/Tookits/TextUtility/Editor.meta
  9. 167 0
      Assets/Tookits/TextUtility/Editor/RichTextEditor.cs
  10. 12 0
      Assets/Tookits/TextUtility/Editor/RichTextEditor.cs.meta
  11. 57 0
      Assets/Tookits/TextUtility/Editor/SpriteInfoManagerWindow.cs
  12. 12 0
      Assets/Tookits/TextUtility/Editor/SpriteInfoManagerWindow.cs.meta
  13. 9 0
      Assets/Tookits/TextUtility/Extensions.meta
  14. 74 0
      Assets/Tookits/TextUtility/Extensions/DictionaryExtension.cs
  15. 12 0
      Assets/Tookits/TextUtility/Extensions/DictionaryExtension.cs.meta
  16. 281 0
      Assets/Tookits/TextUtility/Extensions/ListExtension.cs
  17. 12 0
      Assets/Tookits/TextUtility/Extensions/ListExtension.cs.meta
  18. 55 0
      Assets/Tookits/TextUtility/Extensions/PathExtension.cs
  19. 12 0
      Assets/Tookits/TextUtility/Extensions/PathExtension.cs.meta
  20. 38 0
      Assets/Tookits/TextUtility/Extensions/RegexExtension.cs
  21. 12 0
      Assets/Tookits/TextUtility/Extensions/RegexExtension.cs.meta
  22. 60 0
      Assets/Tookits/TextUtility/Extensions/StringExtension.cs
  23. 12 0
      Assets/Tookits/TextUtility/Extensions/StringExtension.cs.meta
  24. 39 0
      Assets/Tookits/TextUtility/InstanceManager.cs
  25. 12 0
      Assets/Tookits/TextUtility/InstanceManager.cs.meta
  26. 9 0
      Assets/Tookits/TextUtility/KerningSolution.cs
  27. 13 0
      Assets/Tookits/TextUtility/KerningSolution.cs.meta
  28. 9 0
      Assets/Tookits/TextUtility/Match.meta
  29. 36 0
      Assets/Tookits/TextUtility/Match/BaseMatch.cs
  30. 12 0
      Assets/Tookits/TextUtility/Match/BaseMatch.cs.meta
  31. 29 0
      Assets/Tookits/TextUtility/Match/MatchData.cs
  32. 12 0
      Assets/Tookits/TextUtility/Match/MatchData.cs.meta
  33. 26 0
      Assets/Tookits/TextUtility/Match/SegmentMatch.cs
  34. 12 0
      Assets/Tookits/TextUtility/Match/SegmentMatch.cs.meta
  35. 33 0
      Assets/Tookits/TextUtility/Match/SpriteMatch.cs
  36. 12 0
      Assets/Tookits/TextUtility/Match/SpriteMatch.cs.meta
  37. 25 0
      Assets/Tookits/TextUtility/Match/SuperlinkMatch.cs
  38. 12 0
      Assets/Tookits/TextUtility/Match/SuperlinkMatch.cs.meta
  39. 19 0
      Assets/Tookits/TextUtility/Match/TransferSpaceMatch.cs
  40. 12 0
      Assets/Tookits/TextUtility/Match/TransferSpaceMatch.cs.meta
  41. 26 0
      Assets/Tookits/TextUtility/Match/UnderlineMatch.cs
  42. 12 0
      Assets/Tookits/TextUtility/Match/UnderlineMatch.cs.meta
  43. 9 0
      Assets/Tookits/TextUtility/RepeatCallUtility.meta
  44. 264 0
      Assets/Tookits/TextUtility/RepeatCallUtility/RepeatCall.cs
  45. 12 0
      Assets/Tookits/TextUtility/RepeatCallUtility/RepeatCall.cs.meta
  46. 1842 0
      Assets/Tookits/TextUtility/RichText.cs
  47. 12 0
      Assets/Tookits/TextUtility/RichText.cs.meta
  48. 349 0
      Assets/Tookits/TextUtility/RichTextImage.cs
  49. 12 0
      Assets/Tookits/TextUtility/RichTextImage.cs.meta
  50. 161 0
      Assets/Tookits/TextUtility/RichTextImageManager.cs
  51. 12 0
      Assets/Tookits/TextUtility/RichTextImageManager.cs.meta
  52. 9 0
      Assets/Tookits/TextUtility/Setting.meta
  53. 45 0
      Assets/Tookits/TextUtility/Setting/BaseSetting.cs
  54. 12 0
      Assets/Tookits/TextUtility/Setting/BaseSetting.cs.meta
  55. 30 0
      Assets/Tookits/TextUtility/Setting/SpriteSetting.cs
  56. 12 0
      Assets/Tookits/TextUtility/Setting/SpriteSetting.cs.meta
  57. 51 0
      Assets/Tookits/TextUtility/Setting/SuperlinkSetting.cs
  58. 12 0
      Assets/Tookits/TextUtility/Setting/SuperlinkSetting.cs.meta
  59. 52 0
      Assets/Tookits/TextUtility/Setting/UnderlineSetting.cs
  60. 12 0
      Assets/Tookits/TextUtility/Setting/UnderlineSetting.cs.meta
  61. 9 0
      Assets/Tookits/TextUtility/Setting/UnderlineStyle.cs
  62. 13 0
      Assets/Tookits/TextUtility/Setting/UnderlineStyle.cs.meta
  63. 68 0
      Assets/Tookits/TextUtility/SpriteInfo.cs
  64. 12 0
      Assets/Tookits/TextUtility/SpriteInfo.cs.meta
  65. 93 0
      Assets/Tookits/TextUtility/SpriteInfoManager.cs
  66. 12 0
      Assets/Tookits/TextUtility/SpriteInfoManager.cs.meta
  67. 70 0
      Assets/Tookits/TextUtility/SpriteInfoManager.prefab
  68. 8 0
      Assets/Tookits/TextUtility/SpriteInfoManager.prefab.meta
  69. 21 0
      Assets/Tookits/TextUtility/UITest.cs
  70. 12 0
      Assets/Tookits/TextUtility/UITest.cs.meta

+ 74 - 34
Assets/Resource/Prefab/PrefabUI/Canvas.prefab

@@ -2620,7 +2620,7 @@ GameObject:
   m_Component:
   - component: {fileID: 224933538079557970}
   - component: {fileID: 222656634248232074}
-  - component: {fileID: 114254042768240508}
+  - component: {fileID: 114166285046603422}
   m_Layer: 5
   m_Name: Pa_Desc
   m_TagString: Untagged
@@ -25315,6 +25315,79 @@ MonoBehaviour:
       m_Calls: []
     m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0,
       Culture=neutral, PublicKeyToken=null
+--- !u!114 &114166285046603422
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1143182581120354}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 77e6b8885b7d82d45a22af8f91c1408e, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 0.5803922, g: 0.09411766, b: 0.09411766, a: 1}
+  m_RaycastTarget: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+    m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
+      Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+  m_FontData:
+    m_Font: {fileID: 12800000, guid: 0e86defab91f2fb4189708f6dde11005, type: 3}
+    m_FontSize: 15
+    m_FontStyle: 0
+    m_BestFit: 0
+    m_MinSize: 1
+    m_MaxSize: 40
+    m_Alignment: 4
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 0
+    m_VerticalOverflow: 0
+    m_LineSpacing: 1
+  m_Text: 
+  SpriteFoldout: 0
+  SpriteOpenPattern: <(
+  SpriteClosePattern: )>
+  SpriteReplaceChar: 32
+  SpriteSettings: []
+  UnderlineFoldout: 1
+  UnderlineOpenPattern: <[
+  UnderlineClosePattern: ']>'
+  UnderlineSettings:
+  - ID: 0
+    Scale: 0.1
+    Color: {r: 0, g: 0, b: 1, a: 1}
+    Offset: {x: 0, y: 0}
+    Style: 0
+    ExcludeCharASCIIs: 0a000000
+  SuperlinkFoldout: 1
+  SuperlinkOpenPattern: <{
+  SuperlinkClosePattern: '}>'
+  SuperlinkSettings:
+  - ID: 0
+    Scale: 0.75
+    Color: {r: 0, g: 0, b: 1, a: 1}
+    ExcludeCharASCIIs: 0a000000
+    EnableOverflowClick: 0
+  minSize: 10
+  maxSize: 40
+  VerticalBestfitFoldout: 0
+  UseOriginBestfit: 0
+  UseVerticalBestfit: 0
+  VerticalBestfitDirty: 0
+  Debug: 0
+  Foldout: 1
+  CalculateFlag: 0
+  Content: 
+  IDPattern: \(\d+\)
+  KerningSolution: 0
+  ImageManager:
+    Inited: 0
+    RichText: {fileID: 0}
+    Images: []
 --- !u!114 &114168118958650604
 MonoBehaviour:
   m_ObjectHideFlags: 1
@@ -28377,39 +28450,6 @@ MonoBehaviour:
   m_FillAmount: 1
   m_FillClockwise: 1
   m_FillOrigin: 0
---- !u!114 &114254042768240508
-MonoBehaviour:
-  m_ObjectHideFlags: 1
-  m_PrefabParentObject: {fileID: 0}
-  m_PrefabInternal: {fileID: 100100000}
-  m_GameObject: {fileID: 1143182581120354}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_Material: {fileID: 0}
-  m_Color: {r: 0.6, g: 0.10980393, b: 0.10588236, a: 1}
-  m_RaycastTarget: 1
-  m_OnCullStateChanged:
-    m_PersistentCalls:
-      m_Calls: []
-    m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
-      Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
-  m_FontData:
-    m_Font: {fileID: 12800000, guid: 0e86defab91f2fb4189708f6dde11005, type: 3}
-    m_FontSize: 15
-    m_FontStyle: 0
-    m_BestFit: 1
-    m_MinSize: 1
-    m_MaxSize: 21
-    m_Alignment: 4
-    m_AlignByGeometry: 0
-    m_RichText: 1
-    m_HorizontalOverflow: 1
-    m_VerticalOverflow: 1
-    m_LineSpacing: 1
-  m_Text: "\u8863\u670D\u7684\u8BF4\u660E"
 --- !u!114 &114254205725098458
 MonoBehaviour:
   m_ObjectHideFlags: 1

+ 1 - 1
Assets/Resource/XML/lan/ChineseSimplified.xml

@@ -921,7 +921,7 @@
     <Armature1003 desc=""><![CDATA[精灵之翼]]></Armature1003>
     <Armature1004 desc=""><![CDATA[蝴蝶妖精]]></Armature1004>
     <Armature1005 desc=""><![CDATA[蝶梦]]></Armature1005>
-    <DesignByKnife desc=""><![CDATA[热心玩家小刀设计]]></DesignByKnife>
+    <DesignByKnife desc=""><![CDATA[热心玩家<{<color=blue>小刀</color>}>设计]]></DesignByKnife>
   </DressRoom>
   <Tip>
     <Tip1 desc=""><![CDATA[玩累了就休息一下,即使离线,花园也有收益哟~]]></Tip1>

+ 1 - 1
Assets/Resource/XML/lan/ChineseTraditional.xml

@@ -921,7 +921,7 @@
     <Armature1003 desc=""><![CDATA[精靈之翼]]></Armature1003>
     <Armature1004 desc=""><![CDATA[蝴蝶妖精]]></Armature1004>
     <Armature1005 desc=""><![CDATA[蝶夢]]></Armature1005>
-    <DesignByKnife desc=""><![CDATA[熱心玩家小刀設計]]></DesignByKnife>
+    <DesignByKnife desc=""><![CDATA[熱心玩家<{<color=blue>小刀</color>}>設計]]></DesignByKnife>
   </DressRoom>
   <Tip>
     <Tip1 desc=""><![CDATA[玩累了就休息一下,即使離線,花園也有收益喲~]]></Tip1>

+ 1 - 1
Assets/Resource/XML/lan/English.xml

@@ -920,7 +920,7 @@ Increase all Coin prudoction by [&coin_person&](permanently)]]></Pack12>
     <Armature1003 desc=""><![CDATA[Elf Wings]]></Armature1003>
     <Armature1004 desc=""><![CDATA[Butterfly Elf]]></Armature1004>
     <Armature1005 desc=""><![CDATA[Dream of Butterfly]]></Armature1005>
-    <DesignByKnife desc=""><![CDATA[Design by player 小刀]]></DesignByKnife>
+    <DesignByKnife desc=""><![CDATA[Design by player <{<color=blue>小刀</color>}>]]></DesignByKnife>
   </DressRoom>
   <Tip>
     <Tip1 desc=""><![CDATA[The visitors will come to pay the tickest for your garden even if your are offline!]]></Tip1>

BIN
Assets/Resource/Xlsx/language_config.xlsx


+ 3 - 1
Assets/Script/Object/Player.cs

@@ -9,6 +9,7 @@ using System.Xml;
 using System.Linq;
 using System.Collections;
 using System.Collections.Generic;
+using textUtility;
 using Animation = DragonBones.Animation;
 using Slot = DragonBones.Slot;
 using Random = UnityEngine.Random;
@@ -476,7 +477,7 @@ public class CloseItem
         float newSize = PixelSize / Sprites[0].rect.width;
         SetupUI(newSize, new Vector2(0, 22), ResourceManager.Get<Image>(CanvasLabel.Pa_Icon1), ResourceManager.Get<Image>(CanvasLabel.Pa_Icon3), ResourceManager.Get<Image>(CanvasLabel.Pa_Icon2));
 
-        Text descTxt = ResourceManager.Get<Text>(CanvasLabel.Pa_Desc);
+        RichText descTxt = ResourceManager.Get<RichText>(CanvasLabel.Pa_Desc);
         if (string.IsNullOrEmpty(desc))
         {
             descTxt.SetActive(false);
@@ -486,6 +487,7 @@ public class CloseItem
             descTxt.SetActive(true);
             string descContent = Language.GetStr("DressRoom", desc);
             descTxt.text = descContent;
+            descTxt.SetContent(descContent);
         }
 
         ResourceManager.SetText(CanvasLabel.Pa_Lab, Name);

+ 9 - 0
Assets/Tookits/TextUtility.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: afd5f0fc98fd8f249b9374578d844a36
+folderAsset: yes
+timeCreated: 1523165173
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/Editor.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7d311518643d07a44ab41ea17c8c6308
+folderAsset: yes
+timeCreated: 1507815850
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 167 - 0
Assets/Tookits/TextUtility/Editor/RichTextEditor.cs

@@ -0,0 +1,167 @@
+namespace textUtility
+{
+
+
+		using UnityEditor;
+		using UnityEngine;
+		using TextEditor = UnityEditor.UI.TextEditor;
+	
+	[CustomEditor(typeof(RichText))]
+	public class RichTextEditor : TextEditor
+	{
+	    #region Config
+	
+	    private SerializedProperty SpriteSettings;
+	    private SerializedProperty SpriteOpenPattern;
+	    private SerializedProperty SpriteClosePattern;
+	
+	    private SerializedProperty UnderlineSettings;
+	    private SerializedProperty UnderlineOpenPattern;
+	    private SerializedProperty UnderlineClosePattern;
+	
+	    private SerializedProperty SuperlinkSettings;
+	    private SerializedProperty SuperlinkOpenPattern;
+	    private SerializedProperty SuperlinkClosePattern;
+	
+	    private SerializedProperty IDPattern;
+	    private SerializedProperty MinSize;
+	    private SerializedProperty MaxSize;
+	
+	    private RichText Instance;
+	
+	    #endregion
+	
+	    protected override void OnEnable()
+	    {
+	        base.OnEnable();
+	
+	        Instance = (RichText) target;
+	
+	        SpriteSettings = serializedObject.FindProperty("SpriteSettings");
+	        SpriteOpenPattern = serializedObject.FindProperty("SpriteOpenPattern");
+	        SpriteClosePattern = serializedObject.FindProperty("SpriteClosePattern");
+	
+	        UnderlineSettings = serializedObject.FindProperty("UnderlineSettings");
+	        UnderlineOpenPattern = serializedObject.FindProperty("UnderlineOpenPattern");
+	        UnderlineClosePattern = serializedObject.FindProperty("UnderlineClosePattern");
+	
+	        SuperlinkSettings = serializedObject.FindProperty("SuperlinkSettings");
+	        SuperlinkOpenPattern = serializedObject.FindProperty("SuperlinkOpenPattern");
+	        SuperlinkClosePattern = serializedObject.FindProperty("SuperlinkClosePattern");
+	
+	        IDPattern = serializedObject.FindProperty("IDPattern");
+	        MinSize = serializedObject.FindProperty("minSize");
+	        MaxSize = serializedObject.FindProperty("maxSize");
+	    }
+	
+	    public override void OnInspectorGUI()
+	    {
+	        base.OnInspectorGUI();
+	
+	        Instance.Foldout = EditorGUILayout.Foldout(Instance.Foldout, "RichText");
+	        if (Instance.Foldout)
+	        {
+	            Instance.VerticalBestfitFoldout = EditorGUILayout.Foldout(Instance.VerticalBestfitFoldout, "VerticalBestfit");
+	            if (Instance.VerticalBestfitFoldout)
+	            {
+	                EditorGUILayout.BeginHorizontal();
+	                EditorGUILayout.Toggle("Use Vertical Bestfit", Instance.UseVerticalBestfit);
+	                if (Instance.UseVerticalBestfit)
+	                {
+	                    if (GUILayout.Button("Disable"))
+	                    {
+	                        Instance.DisableVerticalBestfit();
+	                    }
+	                    EditorGUILayout.EndHorizontal();
+	                    EditorGUILayout.PropertyField(MinSize);
+	                    EditorGUILayout.PropertyField(MaxSize);
+	                    Instance.ApplyVerticalBestfitSize();
+	                }
+	                else
+	                {
+	                    if (GUILayout.Button("Enable"))
+	                    {
+	                        Instance.EnableVerticalBestfit();
+	                    }
+	                    EditorGUILayout.EndHorizontal();
+	                }
+	            }
+	
+	            Instance.SpriteFoldout = EditorGUILayout.Foldout(Instance.SpriteFoldout, "Sprite");
+	            if (Instance.SpriteFoldout)
+	            {
+	                EditorGUILayout.PropertyField(SpriteOpenPattern);
+	                EditorGUILayout.PropertyField(SpriteClosePattern);
+	                EditorGUILayout.PropertyField(SpriteSettings, true);
+	                if (Instance.SpriteSettings.Count == 0)
+	                {
+	                    Instance.SpriteSettings.Add(new SpriteSetting());
+	                }
+	            }
+	
+	            Instance.UnderlineFoldout = EditorGUILayout.Foldout(Instance.UnderlineFoldout, "Underline");
+	            if (Instance.UnderlineFoldout)
+	            {
+	                EditorGUILayout.PropertyField(UnderlineOpenPattern);
+	                EditorGUILayout.PropertyField(UnderlineClosePattern);
+	                EditorGUILayout.PropertyField(UnderlineSettings, true);
+	                if (Instance.UnderlineSettings.Count == 0)
+	                {
+	                    Instance.UnderlineSettings.Add(new UnderlineSetting());
+	                }
+	            }
+	
+	            Instance.SuperlinkFoldout = EditorGUILayout.Foldout(Instance.SuperlinkFoldout, "Superlink");
+	            if (Instance.SuperlinkFoldout)
+	            {
+	                EditorGUILayout.PropertyField(SuperlinkOpenPattern);
+	                EditorGUILayout.PropertyField(SuperlinkClosePattern);
+	                EditorGUILayout.PropertyField(SuperlinkSettings, true);
+	                if (Instance.SuperlinkSettings.Count == 0)
+	                {
+	                    Instance.SuperlinkSettings.Add(new SuperlinkSetting());
+	                }
+	            }
+	
+	            EditorGUILayout.PropertyField(IDPattern);
+	
+	            if (Instance.Debug)
+	            {
+	                if (GUILayout.Button("DisableDebug"))
+	                {
+	                    OnDisableButtonClick();
+	                }
+	            }
+	            else
+	            {
+	                if (GUILayout.Button("EnableDebug"))
+	                {
+	                    OnEnableButtonClick();
+	                }
+	            }
+	        }
+	
+	        serializedObject.ApplyModifiedProperties();
+	    }
+	
+	    private void OnEnableButtonClick()
+	    {
+	        if (!Application.isPlaying)
+	        {
+	            Instance.EnableDebugMode();
+	            EditorUtility.SetDirty(target);
+	        }
+	    }
+	
+	    private void OnDisableButtonClick()
+	    {
+	        if (!Application.isPlaying)
+	        {
+	            Instance.DisableDebugMode();
+	            EditorUtility.SetDirty(target);
+	        }
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Editor/RichTextEditor.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ce97acc54c904c240843f2cbb58b2dc6
+timeCreated: 1512180275
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 57 - 0
Assets/Tookits/TextUtility/Editor/SpriteInfoManagerWindow.cs

@@ -0,0 +1,57 @@
+namespace textUtility
+{
+
+
+	    using System;
+	    using System.Collections;
+	    using System.Collections.Generic;
+	    using System.IO;
+	    using System.Linq;
+	    using System.Text;
+	    using System.Text.RegularExpressions;
+	    using UnityEditor;
+	    using UnityEngine;
+	
+	    public class SpriteInfoManagerWindow : EditorWindow
+	    {
+	        #region Config
+	
+	        protected SpriteInfoManager Instance;
+	
+	        protected SerializedProperty BuildTestEnviroment;
+	
+	        protected SerializedObject SerializedObject;
+	
+	        #endregion
+	
+	        [MenuItem("DashGame/SpriteInfoManager")]
+	        protected static void ShowWindow()
+	        {
+	            Type inspectorType = Type.GetType("UnityEditor.InspectorWindow,UnityEditor.dll");
+	
+	            SpriteInfoManagerWindow window = GetWindow<SpriteInfoManagerWindow>(inspectorType);
+	
+	            window.titleContent = new GUIContent("SpriteInfoManager");
+	            window.Show();
+	        }
+	
+	        private void OnEnable()
+	        {
+	            Instance = InstanceManager.SearchInstance<SpriteInfoManager>();
+	            SerializedObject = new SerializedObject(Instance);
+	
+	            BuildTestEnviroment = SerializedObject.FindProperty("BuildTestEnviroment");
+	        }
+	
+	        private void OnGUI()
+	        {
+	            SerializedObject.Update();
+	
+	            EditorGUILayout.PropertyField(BuildTestEnviroment, new GUIContent(BuildTestEnviroment.displayName));
+	
+	            SerializedObject.ApplyModifiedProperties();
+	        }
+	    }
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Editor/SpriteInfoManagerWindow.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 1d9206deb6b3aaf4b80ef12448c93d25
+timeCreated: 1512873986
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/Extensions.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b73bfcfca7bed464bb2d911ba5903869
+folderAsset: yes
+timeCreated: 1512968248
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 74 - 0
Assets/Tookits/TextUtility/Extensions/DictionaryExtension.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+public class KVPair<Key, Value>
+{
+    public Key key;
+    public Value value;
+
+    public KVPair()
+    {
+
+    }
+
+    public KVPair(Key key, Value value)
+    {
+        this.key = key;
+        this.value = value;
+    }
+}
+
+public static class DictionaryExtension
+{
+    public static bool IsAvailable<Key, Value>(this Dictionary<Key, Value> dictionary)
+    {
+        return dictionary != null && dictionary.Count > 0;
+    }
+
+    public static KVPair<Key, Value> GetAtIndex<Key, Value>(this Dictionary<Key, Value> dictionary, int index)
+    {
+        List<Key> keys = dictionary.Keys.ToList();
+        List<Value> values = dictionary.Values.ToList();
+        KVPair<Key, Value> kvPair = new KVPair<Key, Value>();
+        kvPair.key = keys[index];
+        kvPair.value = values[index];
+        return kvPair;
+    }
+
+    public static int MyMin<Key, Value>(this Dictionary<Key, Value> dictionary, Func<Key, Value, float> func)
+    {
+        List<Key> keys = dictionary.Keys.ToList();
+        List<Value> values = dictionary.Values.ToList();
+        float min = func(keys[0], values[0]);
+        int minIndex = 0;
+        for (int i = 1; i < keys.Count; i++)
+        {
+            if (min > func(keys[i], values[i]))
+            {
+                min = func(keys[i], values[i]);
+                minIndex = i;
+            }
+        }
+        return minIndex;
+    }
+
+    public static int MyMax<Key, Value>(this Dictionary<Key, Value> dictionary, Func<Key, Value, float> func)
+    {
+        List<Key> keys = dictionary.Keys.ToList();
+        List<Value> values = dictionary.Values.ToList();
+        float max = func(keys[0], values[0]);
+        int maxIndex = 0;
+        for (int i = 1; i < keys.Count; i++)
+        {
+            if (max < func(keys[i], values[i]))
+            {
+                max = func(keys[i], values[i]);
+                maxIndex = i;
+            }
+        }
+        return maxIndex;
+    }
+}

+ 12 - 0
Assets/Tookits/TextUtility/Extensions/DictionaryExtension.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: d45bdb1108a92b946bf89d3dd65be132
+timeCreated: 1512969892
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 281 - 0
Assets/Tookits/TextUtility/Extensions/ListExtension.cs

@@ -0,0 +1,281 @@
+namespace textUtility
+{
+
+
+	using UnityEngine;
+	using UnityEngine.Events;
+	
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	using System.Linq;
+	
+	public static class ListExtension
+	{
+	    public static T Roll<T>(this List<T> list)
+	    {
+	        T t = list[0];
+	        list.RemoveAt(0);
+	        list.Add(t);
+	        return list[0];
+	    }
+	
+	    public static void Switch<T>(this List<T> list, int index0, int index1)
+	    {
+	        T t0 = list[index0];
+	        list[index0] = list[index1];
+	        list[index1] = t0;
+	    }
+	
+	
+	    public static T Prev<T>(this List<T> list, int index)
+	    {
+	        return list[(index + list.Count - 1) % list.Count];
+	    }
+	
+	    public static T Next<T>(this List<T> list, int index)
+	    {
+	        return list[(index + 1) % list.Count];
+	    }
+	
+	
+	    public static T Back<T>(this List<T> list, int index, bool remove = false)
+	    {
+	        T t = list[list.Count - 1 - index];
+	
+	        if (remove)
+	        {
+	            list.RemoveAt(list.Count - 1 - index);
+	        }
+	
+	        return t;
+	    }
+
+        public static T Back<T>(this IList<T> list, int index, bool remove = false)
+        {
+            T t = list[list.Count - 1 - index];
+
+            if (remove)
+            {
+                list.RemoveAt(list.Count - 1 - index);
+            }
+
+            return t;
+        }
+
+        public static T Forward<T>(this List<T> list, int index, bool remove = false)
+	    {
+	        T t = list[index];
+	
+	        if (remove)
+	        {
+	            list.RemoveAt(index);
+	        }
+	
+	        return t;
+	    }
+	
+	    public static List<T> Random<T>(this List<T> list, int amt = 1, bool allDifferent = true, bool remove = false, Func<T,bool> func = null)
+	    {
+	        if (amt > list.Count && allDifferent)
+	        {
+	            throw new Exception();
+	        }
+	
+	
+	        int antiCrush = 0;
+	
+	        List<T> resultList = new List<T>();
+	
+	        while (resultList.Count < amt)
+	        {
+	            if (antiCrush++ > 10000)
+	            {
+	                throw new Exception();
+	            }
+	
+	            int index = UnityEngine.Random.Range(0, list.Count);
+	            
+	            T result = list[index];
+	
+	            if (func != null)
+	            {
+	                if (!func(result))
+	                {
+	                    continue;
+	                }
+	            }
+	
+	            if (allDifferent)
+	            {
+	                if (resultList.Contains(result))
+	                {
+	                    continue;
+	                }
+	            }
+	
+	            if (remove)
+	            {
+	                list.RemoveAt(index);
+	
+	                resultList.Add(result);
+	            }
+	            else
+	            {
+	                resultList.Add(result);
+	            }
+	        }
+	
+	        return resultList;
+	    }
+	
+	    public static List<T> Disturb<T>(this List<T> list)
+	    {
+	        List<T> resultList = new List<T>();
+	
+	        resultList.AddRange(list.Random(list.Count));
+	
+	        return resultList;
+	    }
+		
+	
+	    public static bool IsAvailable<T>(this List<T> list)
+	    {
+	        if (list == null || list.Count == 0)
+	        {
+	            return false;
+	        }
+	        else
+	        {
+	            return true;
+	        }
+	    }
+	
+	    public static bool UniqueAdd<T>(this List<T> list, T obj)
+	    {
+	        if (list.Contains(obj) == false)
+	        {
+	            list.Add(obj);
+	
+	            return true;
+	        }
+	        else
+	        {
+	            return false;
+	        }
+	    }
+	
+	
+	    public static void ForEach<T>(this List<T> list, Action<T> action, bool remove = false)
+	    {
+	        for (int i = 0; i < list.Count; i++)
+	        {
+	            action(list[i]);
+	
+	            if (remove)
+	            {
+	                list.RemoveAt(i--);
+	            }
+	        }
+	    }
+	
+	    public static void BackRemoveAt<T>(this List<T> list, int index)
+	    {
+	        list.RemoveAt(list.Count - 1 - index);
+	    }
+	
+	
+	    public static void MySort<T>(this List<T> list, Func<T, T, bool> func)
+	    {
+	        bool finish = false;
+	
+	        for (int i = 0; i < list.Count; i++)
+	        {
+	            finish = true;
+	
+	            for (int j = 0; j < list.Count - i - 1; j++)
+	            {
+	                if (func(list[j], list[j + 1]))
+	                {
+	                    finish = false;
+	
+	                    T t = list[j];
+	
+	                    list[j] = list[j + 1];
+	                    list[j + 1] = t;
+	                }
+	            }
+	
+	            if (finish)
+	            {
+	                break;
+	            }
+	        }
+	    }
+
+        public static T MyMin<T>(this List<T> list, Func<T, float> func)
+        {
+            float min = func(list[0]);
+            int minIndex = 0;
+
+            for (int i = 1; i < list.Count; i++)
+            {
+                if (min > func(list[i]))
+                {
+                    min = func(list[i]);
+                    minIndex = i;
+                }
+            }
+
+            return list[minIndex];
+        }
+
+        public static T MyMax<T>(this List<T> list, Func<T, float> func)
+        {
+            float max = func(list[0]);
+            int maxIndex = 0;
+
+            for (int i = 1; i < list.Count; i++)
+            {
+                if (max < func(list[i]))
+                {
+                    max = func(list[i]);
+                    maxIndex = i;
+                }
+            }
+
+            return list[maxIndex];
+        }
+
+        public static float MySum<T>(this List<T> list, Func<T,float> func, int count = -1)
+	    {
+	        if (count == -1)
+	        {
+	            count = list.Count;
+	        }
+	
+	        float result = 0;
+	        
+	        for (int i = 0; i < count; i++)
+	        {
+	            result += func(list[i]);
+	        }
+	
+	        return result;
+	    }
+	
+	    public static bool MyContains<T>(this List<T> list, Func<T, bool> func)
+	    {
+	        for (int i = 0; i < list.Count; i++)
+	        {
+	            if (func.Invoke(list[i]))
+	            {
+	                return true;
+	            }
+	        }
+	        return false;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Extensions/ListExtension.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b15380dfd2de1aa469efc71d0b6444e3
+timeCreated: 1512968249
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 55 - 0
Assets/Tookits/TextUtility/Extensions/PathExtension.cs

@@ -0,0 +1,55 @@
+namespace textUtility
+{
+
+
+using System;
+using System.Collections.Generic;
+	using System.IO;
+	
+	using UnityEngine;
+	
+	public static class PathExtension
+	{
+	    public static string GetRelativePath(this string path)
+	    {
+	        return "Assets" + path.Replace(Application.dataPath, "");
+	    }
+	
+	    public static List<string> GetAllFileNameFromPath(this List<string> pathes)
+	    {
+	        List<string> fileName = new List<string>();
+	        foreach (var path in pathes)
+	        {
+	            fileName.Add(Path.GetFileName(path));
+	        }
+	        return fileName;
+	    }
+	
+	    public static string GetUnRepeatFileName(this string fileName)
+	    {
+	        string extension = Path.GetExtension(fileName);
+	        string directory = Path.GetDirectoryName(fileName);
+	        string name = Path.GetFileNameWithoutExtension(fileName);
+	        int count = 1;
+	        string newFileName = string.Format("{0}{1}{2}{3}", directory, Path.DirectorySeparatorChar, name, extension);
+	        while (File.Exists(newFileName))
+	        {
+	            newFileName = string.Format("{0}{1}{2} ({3}){4}", directory, Path.DirectorySeparatorChar, name, count++, extension);
+	        }
+	        return newFileName;
+	    }
+	
+	    public static string GetUnRepeatDirectoryName(this string directoryName)
+	    {
+	        int count = 1;
+	        string newDirectoryName = directoryName;
+	        while (Directory.Exists(newDirectoryName))
+	        {
+	            newDirectoryName = string.Format("{0} ({1})", directoryName, count++);
+	        }
+	        return newDirectoryName;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Extensions/PathExtension.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2e187e8d6103f4e4ba9ebba221a0aff5
+timeCreated: 1512968249
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 38 - 0
Assets/Tookits/TextUtility/Extensions/RegexExtension.cs

@@ -0,0 +1,38 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+
+public static class RegexExtension
+{
+    public static string ToOrPattern(this List<string> patterns)
+    {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.Append("(");
+        for (int i = 0; i < patterns.Count; i++)
+        {
+            stringBuilder.Append(patterns[i]);
+            if (i < patterns.Count - 1)
+            {
+                stringBuilder.Append("|");
+            }
+        }
+        stringBuilder.Append(")");
+        return stringBuilder.ToString();
+    }
+
+    public static string GetTransferedPattern(this string pattern)
+    {
+        pattern = pattern.Replace("(", "\\(");
+        pattern = pattern.Replace(")", "\\)");
+        pattern = pattern.Replace("^", "\\^");
+        pattern = pattern.Replace("+", "\\+");
+        pattern = pattern.Replace("*", "\\*");
+        pattern = pattern.Replace(".", "\\.");
+        pattern = pattern.Replace("[", "\\[");
+        pattern = pattern.Replace("]", "\\]");
+        pattern = pattern.Replace("{", "\\{");
+        pattern = pattern.Replace("}", "\\}");
+        return pattern;
+    }
+}

+ 12 - 0
Assets/Tookits/TextUtility/Extensions/RegexExtension.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 3d3c5484b68529e4e84e9535b711b1bf
+timeCreated: 1512968977
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 60 - 0
Assets/Tookits/TextUtility/Extensions/StringExtension.cs

@@ -0,0 +1,60 @@
+namespace textUtility
+{
+
+
+using System;
+using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public static class StringExtension
+	{
+	    public static bool IsNullOrEmpty(this string str)
+	    {
+	        return string.IsNullOrEmpty(str);
+	    }
+
+        public static string Unwrap(this string str)
+        {
+            int leftIndex = str.IndexOf('(') + 1;
+            int rightIndex = str.LastIndexOf(')') - 1;
+            return str.Substring(leftIndex, rightIndex - leftIndex + 1);
+        }
+
+        public static string Replace(this string str, int index, int length, string newStr)
+        {
+            str = str.Remove(index, length);
+            str = str.Insert(index, newStr);
+            return str;
+        }
+
+    public static string Between(this string str, int startIndex, int endIndex)
+    {
+        if (startIndex > endIndex)
+        {
+            throw new Exception();
+        }
+        else if (startIndex == endIndex)
+        {
+            return str[startIndex].ToString();
+        }
+        else
+        {
+            return str.Substring(startIndex, endIndex - startIndex + 1);
+        }
+    }
+
+    public static List<string> ReplaceAll(this List<string> strings, string oldValue, string newValue)
+	    {
+	        List<string> results = new List<string>();
+	        for (int i = 0; i < strings.Count; i++)
+	        {
+	            results.Add(strings[i].Replace(oldValue, newValue));
+	        }
+	        return results;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Extensions/StringExtension.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b94f3dae2573f9a42aaec47dbb15e24b
+timeCreated: 1512968249
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 39 - 0
Assets/Tookits/TextUtility/InstanceManager.cs

@@ -0,0 +1,39 @@
+namespace textUtility
+{
+
+
+	#if UNITY_EDITOR
+	    using System.Collections.Generic;
+	
+	    using UnityEngine;
+	    using UnityEditor;
+	
+	    using System;
+	    using System.IO;
+	
+	    public class InstanceManager
+	    {
+	        public static T SearchInstance<T>()
+	        {
+	            foreach (var path in Directory.GetFiles(Application.dataPath, "SpriteInfoManager.prefab", SearchOption.AllDirectories))
+	            {
+	                T t = AssetDatabase.LoadAssetAtPath<GameObject>(path.GetRelativePath()).GetComponent<T>();
+	
+	                if (t != null)
+	                {
+	                    return t;
+	                }
+	            }
+	
+	            throw new Exception("找不到SerializeObject");
+	        }
+	
+	        public static string[] SearchAllFiles(string pattern)
+	        {
+	            return Directory.GetFiles(Application.dataPath, pattern, SearchOption.AllDirectories);
+	        }
+	    }
+	#endif
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/InstanceManager.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ceb5945744a93b542aa0ade2acb87644
+timeCreated: 1512873013
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/KerningSolution.cs

@@ -0,0 +1,9 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public enum KerningSolution
+{
+    Precalculate,
+    Shrink,
+}

+ 13 - 0
Assets/Tookits/TextUtility/KerningSolution.cs.meta

@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 821ef2a60ddcf504583c7aa943a3e3a2
+timeCreated: 1515220491
+licenseType: Pro
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/Match.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b0502deefd732de4cb6164abf80a12a4
+folderAsset: yes
+timeCreated: 1512967912
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 36 - 0
Assets/Tookits/TextUtility/Match/BaseMatch.cs

@@ -0,0 +1,36 @@
+namespace textUtility
+{
+
+
+		using System.Collections;
+		using System.Collections.Generic;
+		
+		using UnityEngine;
+	
+	public class BaseMatch
+	{
+	    #region Config
+	
+	    public int Length
+	    {
+	        get { return EndIndex - StartIndex + 1; }
+	    }
+	
+	    public int ID;
+	    public int StartIndex;
+	    public int EndIndex;
+	    public MatchData MatchData;
+	    public List<UIVertex> Vertices;
+	
+	    #endregion
+	
+	    //public BaseMatch(MatchData matchData)
+	    //{
+	    //    StartIndex = matchData.OpenPatternIndex;
+	    //    EndIndex = matchData.OpenPatternIndex + matchData.MatchLength;
+	    //    MatchData = matchData;
+	    //}
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/BaseMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: efaaacae200266e41a53f4eacded4eea
+timeCreated: 1512967959
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 29 - 0
Assets/Tookits/TextUtility/Match/MatchData.cs

@@ -0,0 +1,29 @@
+namespace textUtility
+{
+
+
+		using System.Collections;
+		using System.Collections.Generic;
+		
+		using UnityEngine;
+	
+	public class MatchData
+	{
+	    public int OpenPatternIndex;
+	    public int OpenPatternLength;
+	
+	    public int ClosePatternIndex;
+	    public int ClosePatternLength;
+	
+	    public int ContentIndex;
+	    public int ContentLength;
+	
+	    public int MatchLength;
+	    public string MatchValue;
+	
+	    public string OpenPattern;
+	    public string ClosePattern;
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/MatchData.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 77433e85d4736a24498c78c11185267a
+timeCreated: 1512967922
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 26 - 0
Assets/Tookits/TextUtility/Match/SegmentMatch.cs

@@ -0,0 +1,26 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public class SegmentMatch : BaseMatch
+	{
+        #region Config
+
+        public List<List<int>> Segments = new List<List<int>>();
+        public List<List<Vector3>> RectanglePositionsList = new List<List<Vector3>>();
+
+    #endregion
+
+    //   public SegmentMatch(MatchData matchData) : base(matchData)
+    //{
+
+    //}
+}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/SegmentMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 82a112d18ce12b74484f0c9bfd8e3cbb
+timeCreated: 1512967977
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 33 - 0
Assets/Tookits/TextUtility/Match/SpriteMatch.cs

@@ -0,0 +1,33 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+		using System.Collections.Generic;
+		
+		using UnityEngine;
+	
+	public class SpriteMatch : BaseMatch
+	{
+	    #region Config
+	
+	    public float FrontKerning;
+	    public float RearKerning;
+	    public float ReplaceWidth;
+	    public float ScaledWidth;
+	    public float ScaledHeight;
+	    public string Name;
+	    public SpriteInfo SpriteInfo;
+	    public SpriteSetting SpriteSetting;
+	    public List<Vector3> Positions = new List<Vector3>();
+	
+	    #endregion
+	
+	    //   public SpriteMatch(RichText richText, MatchData matchData) : base(matchData)
+	    //   {
+	
+	    //}
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/SpriteMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 26a9eb02837b7fe4d828f248e62bac8b
+timeCreated: 1512967986
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 25 - 0
Assets/Tookits/TextUtility/Match/SuperlinkMatch.cs

@@ -0,0 +1,25 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public class SuperlinkMatch : SegmentMatch
+	{
+        #region Config
+
+        public SuperlinkSetting SuperlinkSetting;
+
+    #endregion
+
+    //   public SuperlinkMatch(RichText richText, MatchData matchData) : base(matchData)
+    //   {
+
+    //}
+}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/SuperlinkMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5ee5aa6ada274c14fa2966c9851649ae
+timeCreated: 1512968003
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 19 - 0
Assets/Tookits/TextUtility/Match/TransferSpaceMatch.cs

@@ -0,0 +1,19 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public class TransferSpaceMatch : BaseMatch
+	{
+	    //public TransferSpaceMatch(MatchData matchData) : base(matchData)
+	    //{
+
+	    //}
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/TransferSpaceMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9dc6ab30cc5da824cb9a49265ba3475a
+timeCreated: 1512967968
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 26 - 0
Assets/Tookits/TextUtility/Match/UnderlineMatch.cs

@@ -0,0 +1,26 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public class UnderlineMatch : SegmentMatch
+	{
+        #region Config
+
+        public UnderlineSetting UnderlineSetting;
+        public List<List<List<Vector3>>> EllipsePositionsList = new List<List<List<Vector3>>>();
+
+    #endregion
+
+    //   public UnderlineMatch(RichText richText, MatchData matchData) : base(matchData)
+    //   {
+
+    //}
+}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Match/UnderlineMatch.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5dfa73df585b91c4fa93ccc95362bedd
+timeCreated: 1512967995
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/RepeatCallUtility.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d0027fb894448994a93eb944e142e8a1
+folderAsset: yes
+timeCreated: 1512804753
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 264 - 0
Assets/Tookits/TextUtility/RepeatCallUtility/RepeatCall.cs

@@ -0,0 +1,264 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	using UnityEngine;
+	#if UNITY_EDITOR
+	using UnityEditor;
+	#endif
+	
+	public class RepeatCallData
+	{
+	    //public int DelayFrame;
+	    public int DelayFramer;
+	    //public float DelayTime;
+	    public float DelayTimer;
+	
+	    public int RepeatFrame = -1;
+	    public int RepeatFramer;
+	    public float RepeatTime = -1;
+	    public float RepeatTimer;
+	    public Action Action;
+	
+	    public float InvokeCount = Mathf.Infinity;
+	    public float InvokeCounter = Mathf.Infinity;
+	    //public float InvokeFrame = Mathf.Infinity;
+	    //public float InvokeFramer = Mathf.Infinity;
+	    //public float InvokeTime = Mathf.Infinity;
+	    //public float InvokeTimer = Mathf.Infinity;
+	}
+	
+	public class RepeatCall : MonoBehaviour
+	{
+	    #region Config
+	
+	    public static RepeatCall Instance
+	    {
+	        get
+	        {
+	            if (instance == null)
+	            {
+	                GameObject go = new GameObject("RepeatCall");
+	                instance = go.AddComponent<RepeatCall>();
+	                DontDestroyOnLoad(go);
+	            }
+	
+	            return instance;
+	        }
+	        set { instance = value; }
+	    }
+	    private static RepeatCall instance;
+	
+	    private static float DeltaTime
+	    {
+	        get
+	        {
+	            if (Application.isPlaying)
+	            {
+	                return Time.deltaTime;
+	            }
+	            else //�༭��ģʽ��ʹ��
+	            {
+	#if UNITY_EDITOR
+	                double deltaTime = EditorApplication.timeSinceStartup - LastTimeSinceStartup;
+	                LastTimeSinceStartup = EditorApplication.timeSinceStartup;
+	                return (float)deltaTime;
+	#else
+	                throw new Exception();
+	#endif
+	            }
+	        }
+	    }
+	    private static double LastTimeSinceStartup;
+	
+	    private static Dictionary<Coroutine, RepeatCallData> RepeatCallDataDictionary = new Dictionary<Coroutine, RepeatCallData>();
+	
+	#if UNITY_EDITOR
+	    //�༭��ģʽ��ʹ��
+	    private static Dictionary<IEnumerator, EditorApplication.CallbackFunction> CallbackFunctionDictionary = new Dictionary<IEnumerator, EditorApplication.CallbackFunction>();
+	    private static Dictionary<IEnumerator, RepeatCallData> RepeatCallDataDictionaryForEditor = new Dictionary<IEnumerator, RepeatCallData>();
+	#endif
+	
+	    #endregion
+	
+	    public static object Call(RepeatCallData repeatCallData)
+	    {
+	        //repeatCallData.DelayFramer = repeatCallData.DelayFrame;
+	        //repeatCallData.DelayTimer = repeatCallData.DelayTime;
+	        repeatCallData.RepeatFramer = repeatCallData.RepeatFrame;
+	        repeatCallData.RepeatTimer = repeatCallData.RepeatTime;
+	        repeatCallData.InvokeCounter = repeatCallData.InvokeCount;
+	        //repeatCallData.InvokeFramer = repeatCallData.InvokeFrame;
+	        //repeatCallData.InvokeTimer = repeatCallData.InvokeTime;
+	
+	        if (Application.isPlaying)
+	        {
+	            Coroutine coroutine = Instance.StartCoroutine(repeatCall(repeatCallData));
+	            RepeatCallDataDictionary.Add(coroutine, repeatCallData);
+	            return coroutine;
+	        }
+	        else
+	        {
+	#if UNITY_EDITOR
+	            IEnumerator enumerator = repeatCall(repeatCallData);
+	            RepeatCallDataDictionaryForEditor.Add(enumerator, repeatCallData);
+	            EditorApplication.CallbackFunction callbackFunction = () => UpdateEnumerators(enumerator);
+	            CallbackFunctionDictionary.Add(enumerator, callbackFunction);
+	            callbackFunction.Invoke();
+	            EditorApplication.update += callbackFunction;
+	            LastTimeSinceStartup = EditorApplication.timeSinceStartup;
+	            return enumerator;
+	#else
+	            throw new Exception();
+	#endif
+	        }
+	    }
+	
+	    public static void PauseRepeatCall(object obj)
+	    {
+	        if (Application.isPlaying)
+	        {
+	            Instance.StopCoroutine((Coroutine) obj);
+	        }
+	        else
+	        {
+	#if UNITY_EDITOR
+	            EditorApplication.CallbackFunction callbackFunction = CallbackFunctionDictionary[(IEnumerator)obj];
+	            EditorApplication.update -= callbackFunction;
+	#endif
+	        }
+	    }
+	
+	    public static void ResumeRepeatCall(object obj)
+	    {
+	        if (Application.isPlaying)
+	        {
+	            RepeatCallData repeatCallData = RepeatCallDataDictionary[(Coroutine)obj];
+	            Call(repeatCallData);
+	        }
+	        else
+	        {
+	#if UNITY_EDITOR
+	            RepeatCallData repeatCallData = RepeatCallDataDictionaryForEditor[(IEnumerator)obj];
+	            Call(repeatCallData);
+	#endif
+	        }
+	    }
+	
+	    private static void UpdateEnumerators(IEnumerator enumerator) //�༭��ģʽ��ʹ��
+	    {
+	        enumerator.MoveNext();
+	    }
+	
+	    private static IEnumerator repeatCall(RepeatCallData repeatCallData)
+	    {
+	        //Debug.Log(repeatCallData.DelayFramer + "  " + repeatCallData.DelayTimer);
+	        if (repeatCallData.DelayFramer > 0)
+	        {
+	            while (repeatCallData.DelayFramer > 0)
+	            {
+	                repeatCallData.DelayFramer--;
+	                yield return null;
+	            }
+	            if (InvokeRepeatCall(repeatCallData))
+	            {
+	                yield break;
+	            }
+	        }
+	        else if (repeatCallData.DelayTimer > 0)
+	        {
+	            yield return null; //����StartCoroutine��������ִ�е���һ��yield
+	            while (repeatCallData.DelayTimer > 0)
+	            {
+	                repeatCallData.DelayTimer -= DeltaTime;
+	                yield return null;
+	            }
+	            if (InvokeRepeatCall(repeatCallData))
+	            {
+	                yield break;
+	            }
+	        }
+	        else
+	        {
+	            if (InvokeRepeatCall(repeatCallData))
+	            {
+	                yield break;
+	            }
+	            else
+	            {
+	                yield return null;
+	            }
+	
+	            if (repeatCallData.RepeatFramer > -1)
+	            {
+	                while (repeatCallData.RepeatFramer > 0)
+	                {
+	                    repeatCallData.RepeatFramer--;
+	                    yield return null;
+	                }
+	                repeatCallData.RepeatFramer = repeatCallData.RepeatFrame;
+	                if (InvokeRepeatCall(repeatCallData))
+	                {
+	                    yield break;
+	                }
+	            }
+	            else if (repeatCallData.RepeatTimer > -1)
+	            {
+	                while (repeatCallData.RepeatTimer > 0)
+	                {
+	                    repeatCallData.RepeatTimer -= DeltaTime;
+	                    yield return null;
+	                }
+	                repeatCallData.RepeatTimer = repeatCallData.RepeatTime;
+	                if (InvokeRepeatCall(repeatCallData))
+	                {
+	                    yield break;
+	                }
+	            }
+	            else
+	            {
+	                throw new Exception();
+	            }
+	        }
+	    }
+	
+	    private static bool InvokeRepeatCall(RepeatCallData repeatCallData)
+	    {
+	        repeatCallData.Action.Invoke();
+	        if (--repeatCallData.InvokeCounter <= 0)
+	        {
+	            return true;
+	        }
+	        else
+	        {
+	            return false;
+	        }
+	    }
+	}
+	
+	public class DelayCall
+	{
+	    public static object Call(int delayFrame, Action action)
+	    {
+	        RepeatCallData repeatCallData = new RepeatCallData();
+	        repeatCallData.DelayFramer = delayFrame;
+	        repeatCallData.InvokeCount = 1;
+	        repeatCallData.Action = action;
+	        return RepeatCall.Call(repeatCallData);
+	    }
+	
+	    public static object Call(float delayTime, Action action)
+	    {
+	        RepeatCallData repeatCallData = new RepeatCallData();
+	        repeatCallData.DelayTimer = delayTime;
+	        repeatCallData.InvokeCount = 1;
+	        repeatCallData.Action = action;
+	        return RepeatCall.Call(repeatCallData);
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/RepeatCallUtility/RepeatCall.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: eea64f5d9e49bbe4cbcf27e11e166e3a
+timeCreated: 1512348444
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1842 - 0
Assets/Tookits/TextUtility/RichText.cs

@@ -0,0 +1,1842 @@
+using UnityEngine.EventSystems;
+
+namespace textUtility
+{
+
+
+		using System;
+		using System.Collections.Generic;
+		using System.Text;
+		using System.Text.RegularExpressions;
+		using UnityEditor.VisualStudioIntegration;
+		using UnityEngine;
+		using UnityEngine.UI;
+	
+	[Serializable]
+	public class RichText : Text, IPointerClickHandler
+	{
+	    private class ParseResult
+	    {
+	        public int ExtraOffset;
+	        public string NewText;
+	        public BaseMatch Match;
+	    }
+	
+	    #region Config
+	
+	    private List<string> OpenPatterns
+	    {
+	        get
+	        {
+	            if (!openPatterns.IsAvailable())
+	            {
+	                openPatterns = new List<string>();
+	                openPatterns.Add(SpriteOpenPattern);
+	                openPatterns.Add(UnderlineOpenPattern);
+	                openPatterns.Add(SuperlinkOpenPattern);
+	            }
+	            return openPatterns;
+	        }
+	    }
+	    private List<string> openPatterns;
+	
+	    private List<string> ClosePatterns
+	    {
+	        get
+	        {
+	            if (!closePatterns.IsAvailable())
+	            {
+	                closePatterns = new List<string>();
+	                closePatterns.Add(SpriteClosePattern);
+	                closePatterns.Add(UnderlineClosePattern);
+	                closePatterns.Add(SuperlinkClosePattern);
+	            }
+	            return closePatterns;
+	        }
+	    }
+	    private List<string> closePatterns;
+	
+	    private Dictionary<string, Func<string, MatchData, ParseResult>> OpenCloseParseFunctionDictionary
+	    {
+	        get
+	        {
+	            if (!openCloseParseFunctionDictionary.IsAvailable())
+	            {
+	                openCloseParseFunctionDictionary = new Dictionary<string, Func<string, MatchData, ParseResult>>();
+	                openCloseParseFunctionDictionary.Add(SpriteOpenPattern, ParseSpritePattern);
+	                openCloseParseFunctionDictionary.Add(UnderlineOpenPattern, ParseUnderlinePattern);
+	                openCloseParseFunctionDictionary.Add(SuperlinkOpenPattern, ParseSuperlinkPattern);
+	            }
+	            return openCloseParseFunctionDictionary;
+	        }
+	    }
+	    private Dictionary<string, Func<string, MatchData, ParseResult>> openCloseParseFunctionDictionary;
+	
+	    public bool SpriteFoldout;
+	    public string SpriteOpenPattern = "<(";
+	    public string SpriteClosePattern = ")>";
+	    public char SpriteReplaceChar = ' ';
+	    public List<SpriteSetting> SpriteSettings; //todo 可能为空
+	    private Dictionary<int, SpriteSetting> SpriteSettingDictionary
+	    {
+	        get
+	        {
+	            if (!spriteSettingDictionary.IsAvailable())
+	            {
+	                spriteSettingDictionary = new Dictionary<int, SpriteSetting>();
+	                foreach (var item in SpriteSettings)
+	                {
+	                    spriteSettingDictionary.Add(item.ID, item);
+	                }
+	            }
+	            return spriteSettingDictionary;
+	        }
+	        set { spriteSettingDictionary = value; }
+	    }
+	    private Dictionary<int, SpriteSetting> spriteSettingDictionary;
+	    private List<BaseMatch> AvailableSpriteMatches = new List<BaseMatch>();
+	    private List<BaseMatch> TruncateSpriteMatches = new List<BaseMatch>();
+	
+	    public bool UnderlineFoldout;
+	    public string UnderlineOpenPattern = "<[";
+	    public string UnderlineClosePattern = "]>";
+	    public List<UnderlineSetting> UnderlineSettings; //todo 可能为空
+	    private Dictionary<int, UnderlineSetting> UnderlineSettingDictionary
+	    {
+	        get
+	        {
+	            if (!underlineSettingDictionary.IsAvailable())
+	            {
+	                underlineSettingDictionary = new Dictionary<int, UnderlineSetting>();
+	                foreach (var item in UnderlineSettings)
+	                {
+	                    underlineSettingDictionary.Add(item.ID, item);
+	                }
+	            }
+	            return underlineSettingDictionary;
+	        }
+	        set { underlineSettingDictionary = value; }
+	    }
+	    private Dictionary<int, UnderlineSetting> underlineSettingDictionary;
+	    private List<BaseMatch> UnderlineMatches = new List<BaseMatch>();
+	
+	    public bool SuperlinkFoldout;
+	    public string SuperlinkOpenPattern = "<{";
+	    public string SuperlinkClosePattern = "}>";
+	    public List<SuperlinkSetting> SuperlinkSettings; //todo 可能为空
+	    public Dictionary<int, Action<int>> SuperlinkCallbackDictionary = new Dictionary<int, Action<int>>();
+	    private Dictionary<int, SuperlinkSetting> SuperlinkSettingDictionary
+	    {
+	        get
+	        {
+	            if (superlinkSettingDictionary == null)
+	            {
+	                superlinkSettingDictionary = new Dictionary<int, SuperlinkSetting>();
+	                foreach (var item in SuperlinkSettings)
+	                {
+	                    superlinkSettingDictionary.Add(item.ID, item);
+	                }
+	            }
+	            return superlinkSettingDictionary;
+	        }
+	    }
+	    private Dictionary<int, SuperlinkSetting> superlinkSettingDictionary;
+	    private List<BaseMatch> SuperlinkMatches = new List<BaseMatch>();
+	
+	    private char TransferSpaceReplaceChar = 'i';
+	    private List<BaseMatch> TransferSpaceMatches = new List<BaseMatch>();
+	    private List<List<BaseMatch>> MatchesList = new List<List<BaseMatch>>();
+	
+	    public int MinSize
+	    {
+	        get { return minSize; }
+	        set
+	        {
+	            minSize = value;
+	            ApplyVerticalBestfitSize();
+	        }
+	    }
+	    public int minSize = 10;
+	
+	    public int MaxSize
+	    {
+	        get { return maxSize; }
+	        set
+	        {
+	            maxSize = value;
+	            ApplyVerticalBestfitSize();
+	        }
+	    }
+	    public int maxSize = 40;
+	
+	    public bool VerticalBestfitFoldout;
+	    public bool UseOriginBestfit;
+	    public bool UseVerticalBestfit;
+	    public bool VerticalBestfitDirty;
+	
+	    public bool Debug;
+	    public bool Foldout = true;
+	    public bool CalculateFlag;
+	    public string Content;
+	    public string IDPattern = "\\(\\d+\\)";
+	    public KerningSolution KerningSolution;
+	    public RichTextImageManager ImageManager;
+	
+	    private char CalculateReferenceChar = 'i';
+	    private TextGenerator CalculateTextGenerator;
+	
+	    #endregion
+	
+	    protected override void Awake()
+	    {
+	        base.Awake();
+	
+	        if (Debug)
+	        {
+	            text = Content;
+	            UnityEngine.Debug.LogWarning("没有关闭Debug", gameObject);
+	        }
+	    }
+	
+	    protected override void OnPopulateMesh(VertexHelper toFill)
+	    {
+	        if (CalculateFlag)
+	        {
+	            CalculateFlag = false;
+	        }
+	        else
+	        {
+	            string newText = Content;
+
+	            if (Application.isPlaying || Debug)
+	            {
+	                bool addNewOpenCloseMatch = ParseAndFillMatches(ref newText);
+	                if (addNewOpenCloseMatch)
+	                {
+	                    AddNewOpenCloseMatch(newText);
+	                }
+	                CalculateFlag = addNewOpenCloseMatch;
+	            }
+	
+	            if (UseVerticalBestfit && horizontalOverflow != HorizontalWrapMode.Overflow && verticalOverflow != VerticalWrapMode.Overflow)
+	            {
+	                if (VerticalBestfitDirty)
+	                {
+	                    int verticalBestfitFontSize = GetVerticalBestfitFontSize(newText);
+	                    if (fontSize != verticalBestfitFontSize)
+	                    {
+	                        DelayCall.Call(1, () => { fontSize = verticalBestfitFontSize; });
+	                        VerticalBestfitDirty = false;
+	                        CalculateFlag = true;
+	                    }
+	                }
+	                else
+	                {
+	                    VerticalBestfitDirty = true;
+	                }
+	            }
+	
+	            if (CalculateFlag)
+	            {
+	                return;
+	            }
+	        }
+	
+	        base.OnPopulateMesh(toFill);
+	
+	        List<UIVertex> vertices = new List<UIVertex>();
+	        toFill.GetUIVertexStream(vertices);
+	        ImageManager.Vertices = vertices;
+	
+	        HandleTransferSpaceMatch(toFill, vertices);
+	
+	        for (int i = 0; i < vertices.Count; i += 6)
+	        {
+	            Vector3 position0 = transform.TransformPoint(vertices[0 + i].position);
+	            Vector3 position1 = transform.TransformPoint(vertices[1 + i].position);
+	            Vector3 position2 = transform.TransformPoint(vertices[2 + i].position);
+	            Vector3 position3 = transform.TransformPoint(vertices[4 + i].position);
+	
+	            UnityEngine.Debug.DrawLine(position0, position1, Color.black, 1);
+	            UnityEngine.Debug.DrawLine(position1, position2, Color.black, 1);
+	            UnityEngine.Debug.DrawLine(position2, position3, Color.black, 1);
+	            UnityEngine.Debug.DrawLine(position3, position0, Color.black, 1);
+	        }
+	
+	        if (KerningSolution == KerningSolution.Precalculate)
+	        {
+	            for (int i = 0; i < AvailableSpriteMatches.Count; i++)
+	            {
+	                SpriteMatch spriteMatch = (SpriteMatch)AvailableSpriteMatches[i];
+	                int index0 = 4 + spriteMatch.StartIndex * 6;
+	                int index1 = 3 + spriteMatch.EndIndex * 6;
+	                Vector3 leftPosition = vertices[index0].position;
+	                Vector3 rightPosition = vertices[index1].position;
+	                Vector3 bottomMiddle = (leftPosition + rightPosition) / 2;
+	                Vector3 position0;
+	                Vector3 position1;
+	                Vector3 position2;
+	                Vector3 position3;
+	                spriteMatch.ScaledHeight = spriteMatch.SpriteInfo.GetScaledHeight(spriteMatch.ScaledWidth);
+	                position0 = bottomMiddle + new Vector3(-spriteMatch.ScaledWidth / 2, 0, 0);
+	                position1 = bottomMiddle + new Vector3(spriteMatch.ScaledWidth / 2, 0, 0);
+	                position2 = bottomMiddle + new Vector3(spriteMatch.ScaledWidth / 2, spriteMatch.ScaledHeight, 0);
+	                position3 = bottomMiddle + new Vector3(-spriteMatch.ScaledWidth / 2, spriteMatch.ScaledHeight, 0);
+	                position0 = transform.TransformPoint(position0);
+	                position1 = transform.TransformPoint(position1);
+	                position2 = transform.TransformPoint(position2);
+	                position3 = transform.TransformPoint(position3);
+	                spriteMatch.Positions.Add(position0);
+	                spriteMatch.Positions.Add(position1);
+	                spriteMatch.Positions.Add(position2);
+	                spriteMatch.Positions.Add(position3);
+	            }
+	        }
+	        else if (KerningSolution == KerningSolution.Shrink)
+	        {
+	            Dictionary<SpriteInfo, Dictionary<int, List<SpriteMatch>>> dictionary = new Dictionary<SpriteInfo, Dictionary<int, List<SpriteMatch>>>();
+	            for (int i = 0; i < AvailableSpriteMatches.Count; i++)
+	            {
+	                SpriteMatch spriteMatch = (SpriteMatch)AvailableSpriteMatches[i];
+	                if (dictionary.ContainsKey(spriteMatch.SpriteInfo))
+	                {
+	                    if (dictionary[spriteMatch.SpriteInfo].ContainsKey(spriteMatch.Length))
+	                    {
+	                        dictionary[spriteMatch.SpriteInfo][spriteMatch.Length].Add(spriteMatch);
+	                    }
+	                    else
+	                    {
+	                        List<SpriteMatch> matches = new List<SpriteMatch>();
+	                        matches.Add(spriteMatch);
+	                        dictionary[spriteMatch.SpriteInfo].Add(spriteMatch.Length, matches);
+	                    }
+	                }
+	                else
+	                {
+	                    List<SpriteMatch> matches = new List<SpriteMatch>();
+	                    matches.Add(spriteMatch);
+	                    Dictionary<int, List<SpriteMatch>> lengthDictionary = new Dictionary<int, List<SpriteMatch>>();
+	                    lengthDictionary.Add(spriteMatch.Length, matches);
+	                    dictionary.Add(spriteMatch.SpriteInfo, lengthDictionary);
+	                }
+	            }
+	
+	            foreach (var lengthDictionary in dictionary)
+	            {
+	                foreach (var matches in lengthDictionary.Value.Values)
+	                {
+	                    float minScaledWidth = Mathf.Infinity;
+	                    for (int i = 0; i < matches.Count; i++)
+	                    {
+	                        float scaledWidth = matches[i].ScaledWidth;
+	                        int frontIndex = matches[i].StartIndex - 1;
+	                        int rearIndex = matches[i].EndIndex + 1;
+	                        float frontKerning;
+	                        float rearKerning;
+	                        if (frontIndex < 0)
+	                        {
+	                            frontKerning = 0;
+	                        }
+	                        else
+	                        {
+	                            frontKerning = GetKerning(text[frontIndex], SpriteReplaceChar);
+	                        }
+	                        if (rearIndex >= text.Length)
+	                        {
+	                            rearKerning = 0;
+	                        }
+	                        else
+	                        {
+	                            rearKerning = GetKerning(SpriteReplaceChar, text[rearIndex]);
+	                        }
+	                        if (frontKerning < 0)
+	                        {
+	                            scaledWidth += frontKerning;
+	                        }
+	                        if (rearKerning < 0)
+	                        {
+	                            scaledWidth += rearKerning;
+	                        }
+	                        if (minScaledWidth > scaledWidth)
+	                        {
+	                            minScaledWidth = scaledWidth;
+	                        }
+	                        matches[i].FrontKerning = frontKerning;
+	                        matches[i].RearKerning = rearKerning;
+	                    }
+	                    float minScaledHeight = lengthDictionary.Key.GetScaledHeight(minScaledWidth);
+	                    for (int i = 0; i < matches.Count; i++)
+	                    {
+	                        matches[i].ScaledWidth = minScaledWidth;
+	                        matches[i].ScaledHeight = minScaledHeight;
+	                    }
+	                }
+	            }
+	
+	            for (int i = 0; i < AvailableSpriteMatches.Count; i++)
+	            {
+	                SpriteMatch spriteMatch = (SpriteMatch)AvailableSpriteMatches[i];
+	                int index0 = 4 + spriteMatch.StartIndex * 6;
+	                int index1 = 3 + spriteMatch.EndIndex * 6;
+	                Vector3 leftPosition = vertices[index0].position;
+	                Vector3 rightPosition = vertices[index1].position;
+	                leftPosition.x -= spriteMatch.FrontKerning;
+	                rightPosition.x += spriteMatch.RearKerning;
+	                Vector3 bottomMiddle = (leftPosition + rightPosition) / 2;
+	                Vector3 position0;
+	                Vector3 position1;
+	                Vector3 position2;
+	                Vector3 position3;
+	                position0 = bottomMiddle + new Vector3(-spriteMatch.ScaledWidth / 2, 0, 0);
+	                position1 = bottomMiddle + new Vector3(spriteMatch.ScaledWidth / 2, 0, 0);
+	                position2 = bottomMiddle + new Vector3(spriteMatch.ScaledWidth / 2, spriteMatch.ScaledHeight, 0);
+	                position3 = bottomMiddle + new Vector3(-spriteMatch.ScaledWidth / 2, spriteMatch.ScaledHeight, 0);
+	                position0 = transform.TransformPoint(position0);
+	                position1 = transform.TransformPoint(position1);
+	                position2 = transform.TransformPoint(position2);
+	                position3 = transform.TransformPoint(position3);
+	                spriteMatch.Positions.Add(position0);
+	                spriteMatch.Positions.Add(position1);
+	                spriteMatch.Positions.Add(position2);
+	                spriteMatch.Positions.Add(position3);
+	            }
+	        }
+	
+	        for (int i = 0; i < UnderlineMatches.Count; i++)
+	        {
+	            UnderlineMatch match = UnderlineMatches[i] as UnderlineMatch;
+	            //print(match.StartIndex + "  " + match.EndIndex);
+	            GetSegments(text, CalculateTextGenerator, match, match.UnderlineSetting.UnderlineExcludeChars);
+	            for (int j = 0; j < match.Segments.Count; j++)
+	            {
+	                int startCharIdx = match.Segments[j][0];
+	                int endCharIdx = match.Segments[j][1];
+	                //print(startCharIdx + "  " + endCharIdx);
+	                int lineIndex = GetLineIndex(CalculateTextGenerator, startCharIdx);
+	                int lineEndCharacterIndex;
+	                float penPositionY = GetPenPositionY(lineIndex, CalculateTextGenerator.lineCount);
+	                float lineHeight = CalculateTextGenerator.lines[lineIndex].height;
+	                if (lineIndex != -1)
+	                {
+	                    lineEndCharacterIndex = GetLineEndCharacterIndex(CalculateTextGenerator, lineIndex, text.Length);
+	                }
+	                else
+	                {
+	                    break;
+	                }
+	                int newEndCharIdx = Mathf.Min(lineEndCharacterIndex, endCharIdx);
+	                List<Vector3> positions = new List<Vector3>();
+	                Vector3 position0 = vertices[4 + startCharIdx * 6].position;
+	                Vector3 position1 = vertices[2 + newEndCharIdx * 6].position;
+	                position0.y = penPositionY;
+	                position1.y = penPositionY;
+	                Vector3 position2 = position1;
+	                Vector3 position3 = position0;
+	                float underlineHeight = lineHeight * match.UnderlineSetting.Scale;
+	                position2.y -= underlineHeight;
+	                position3.y -= underlineHeight;
+	                if (match.UnderlineSetting.Style == UnderlineStyle.Ellipse)
+	                {
+	                    if (vertices[1 + endCharIdx*6].position.x - vertices[0 + startCharIdx*6].position.x > underlineHeight*2)
+	                    {
+	                        position0.x += underlineHeight;
+	                        position1.x -= underlineHeight;
+	                        position2.x -= underlineHeight;
+	                        position3.x += underlineHeight;
+	                        List<Vector3> topHemispherePositions = new List<Vector3>();
+	                        List<Vector3> bottomHemispherePositions = new List<Vector3>();
+	                        int cutCount = Mathf.Max(4, Mathf.CeilToInt(underlineHeight));
+	                        float radius = underlineHeight/2;
+	                        for (int k = 0; k <= cutCount; k++)
+	                        {
+	                            float angle = 90f/cutCount*k;
+	                            float radian = angle*Mathf.Deg2Rad;
+	                            float x = radius * Mathf.Sin(radian);
+	                            float y = radius * Mathf.Cos(radian);
+	                            topHemispherePositions.Add(new Vector3(x, y, 0));
+	                            if (k != cutCount)
+	                            {
+	                                bottomHemispherePositions.Insert(0, new Vector3(x, -y, 0));
+	                            }
+	                        }
+	                        List<List<Vector3>> ellipsePositionsList = new List<List<Vector3>>();
+	                        List<Vector3> ellipsePositions = new List<Vector3>(); //右半球
+	                        ellipsePositions.Add((position1+position2)/2);
+	                        foreach (var position in topHemispherePositions)
+	                        {
+	                            Vector3 newPosition = ellipsePositions[0] + position;
+	                            ellipsePositions.Add(newPosition);
+	                        }
+	                        //ellipsePositions.Add(ellipsePositions[0] + new Vector3(radius, 0, 0));
+	                        foreach (var position in bottomHemispherePositions)
+	                        {
+	                            Vector3 newPosition = ellipsePositions[0] + position;
+	                            ellipsePositions.Add(newPosition);
+	                        }
+	                        for (int k = 0; k < ellipsePositions.Count; k++)
+	                        {
+	                            ellipsePositions[k] = rectTransform.TransformPoint(ellipsePositions[k]);
+	                        }
+	                        ellipsePositionsList.Add(ellipsePositions);
+	                        ellipsePositions = new List<Vector3>(); //左半球
+	                        ellipsePositions.Add((position0 + position3) / 2);
+	                        foreach (var position in topHemispherePositions)
+	                        {
+	                            Vector3 newPosition = ellipsePositions[0];
+	                            newPosition.x -= position.x;
+	                            newPosition.y += position.y;
+	                            ellipsePositions.Add(newPosition);
+	                        }
+	                        //ellipsePositions.Add(ellipsePositions[0] + new Vector3(-radius, 0, 0));
+	                        foreach (var position in bottomHemispherePositions)
+	                        {
+	                            Vector3 newPosition = ellipsePositions[0];
+	                            newPosition.x -= position.x;
+	                            newPosition.y += position.y;
+	                            ellipsePositions.Add(newPosition);
+	                        }
+	                        for (int k = 0; k < ellipsePositions.Count; k++)
+	                        {
+	                            ellipsePositions[k] = rectTransform.TransformPoint(ellipsePositions[k]);
+	                        }
+	                        ellipsePositionsList.Add(ellipsePositions);
+	                        match.EllipsePositionsList.Add(ellipsePositionsList);
+	                    }
+	                }
+	                positions.Add(position0);
+	                positions.Add(position1);
+	                positions.Add(position2);
+	                positions.Add(position3);
+	                positions[0] = rectTransform.TransformPoint(positions[0]);
+	                positions[1] = rectTransform.TransformPoint(positions[1]);
+	                positions[2] = rectTransform.TransformPoint(positions[2]);
+	                positions[3] = rectTransform.TransformPoint(positions[3]);
+	                match.RectanglePositionsList.Add(positions);
+	                if (endCharIdx > lineEndCharacterIndex)
+	                {
+	                    break;
+	                }
+	            }
+	        }
+	        //print(SuperlinkMatches.Count);
+	        for (int i = 0; i < SuperlinkMatches.Count; i++)
+	        {
+	            SuperlinkMatch match = SuperlinkMatches[i] as SuperlinkMatch;
+	            //print(match.StartIndex + "  " + match.EndIndex);
+	            GetSegments(text, CalculateTextGenerator, match, match.SuperlinkSetting.SuperlinkExcludeChars);
+	            for (int j = 0; j < match.Segments.Count; j++)
+	            {
+	                int startCharIdx = match.Segments[j][0];
+	                int endCharIdx = match.Segments[j][1];
+	                //print(startCharIdx + "  " + endCharIdx);
+	                int lineIndex = GetLineIndex(CalculateTextGenerator, startCharIdx);
+	                int lineEndCharacterIndex;
+	                float lineCenterY = GetLineCenterY(lineIndex, CalculateTextGenerator.lineCount);
+	                float lineHeight = CalculateTextGenerator.lines[lineIndex].height;
+	                if (lineIndex != -1)
+	                {
+	                    lineEndCharacterIndex = GetLineEndCharacterIndex(CalculateTextGenerator, lineIndex, text.Length);
+	                }
+	                else
+	                {
+	                    break;
+	                }
+	                int newEndCharIdx = Mathf.Min(lineEndCharacterIndex, endCharIdx);
+	                List<Vector3> positions = new List<Vector3>();
+	                float lineMaxY = lineCenterY + (lineHeight / 2) * match.SuperlinkSetting.Scale;
+	                float lineMinY = lineCenterY - (lineHeight / 2) * match.SuperlinkSetting.Scale;
+	                Vector3 position0 = vertices[4 + startCharIdx * 6].position;
+	                Vector3 position1 = vertices[2 + newEndCharIdx * 6].position;
+	                Vector3 position2 = position1;
+	                Vector3 position3 = position0;
+	                position0.y = lineMaxY;
+	                position1.y = lineMaxY;
+	                position2.y = lineMinY;
+	                position3.y = lineMinY;
+	                positions.Add(position0);
+	                positions.Add(position1);
+	                positions.Add(position2);
+	                positions.Add(position3);
+	                positions[0] = rectTransform.TransformPoint(positions[0]);
+	                positions[1] = rectTransform.TransformPoint(positions[1]);
+	                positions[2] = rectTransform.TransformPoint(positions[2]);
+	                positions[3] = rectTransform.TransformPoint(positions[3]);
+	                match.RectanglePositionsList.Add(positions);
+	                if (endCharIdx > lineEndCharacterIndex)
+	                {
+	                    break;
+	                }
+	            }
+	        }
+	
+	        for (int i = 0; i < AvailableSpriteMatches.Count; i++)
+	        {
+	            SpriteMatch match = (SpriteMatch)AvailableSpriteMatches[i];
+	            Vector3 position0 = match.Positions[0];
+	            Vector3 position1 = match.Positions[1];
+	            Vector3 position2 = match.Positions[2];
+	            Vector3 position3 = match.Positions[3];
+	            UnityEngine.Debug.DrawLine(position0, position1, Color.red, 1);
+	            UnityEngine.Debug.DrawLine(position1, position2, Color.red, 1);
+	            UnityEngine.Debug.DrawLine(position2, position3, Color.red, 1);
+	            UnityEngine.Debug.DrawLine(position3, position0, Color.red, 1);
+	        }
+	
+	        for (int i = 0; i < UnderlineMatches.Count; i++)
+	        {
+	            UnderlineMatch match = (UnderlineMatch)UnderlineMatches[i];
+	            for (int j = 0; j < match.RectanglePositionsList.Count; j++)
+	            {
+	                Vector3 position0 = match.RectanglePositionsList[j][0];
+	                Vector3 position1 = match.RectanglePositionsList[j][1];
+	                Vector3 position2 = match.RectanglePositionsList[j][2];
+	                Vector3 position3 = match.RectanglePositionsList[j][3];
+	                UnityEngine.Debug.DrawLine(position0, position1, Color.blue, 1);
+	                UnityEngine.Debug.DrawLine(position1, position2, Color.blue, 1);
+	                UnityEngine.Debug.DrawLine(position2, position3, Color.blue, 1);
+	                UnityEngine.Debug.DrawLine(position3, position0, Color.blue, 1);
+	            }
+	            for (int j = 0; j < match.EllipsePositionsList.Count; j++)
+	            {
+	                for (int k = 0; k < match.EllipsePositionsList[j].Count; k++)
+	                {
+	                    for (int l = 1; l < match.EllipsePositionsList[j][k].Count - 1; l++)
+	                    {
+	                        Vector3 position0 = match.EllipsePositionsList[j][k][0];
+	                        Vector3 position1 = match.EllipsePositionsList[j][k][l + 0];
+	                        Vector3 position2 = match.EllipsePositionsList[j][k][l + 1];
+	                        UnityEngine.Debug.DrawLine(position0, position1, Color.blue, 1);
+	                        UnityEngine.Debug.DrawLine(position1, position2, Color.blue, 1);
+	                        UnityEngine.Debug.DrawLine(position2, position0, Color.blue, 1);
+	                    }
+	                }
+	            }
+	        }
+	
+	        for (int i = 0; i < SuperlinkMatches.Count; i++)
+	        {
+	            SuperlinkMatch match = (SuperlinkMatch)SuperlinkMatches[i];
+	            for (int j = 0; j < match.RectanglePositionsList.Count; j++)
+	            {
+	                Vector3 position0 = match.RectanglePositionsList[j][0];
+	                Vector3 position1 = match.RectanglePositionsList[j][1];
+	                Vector3 position2 = match.RectanglePositionsList[j][2];
+	                Vector3 position3 = match.RectanglePositionsList[j][3];
+	                UnityEngine.Debug.DrawLine(position0, position1, Color.green, 1);
+	                UnityEngine.Debug.DrawLine(position1, position2, Color.green, 1);
+	                UnityEngine.Debug.DrawLine(position2, position3, Color.green, 1);
+	                UnityEngine.Debug.DrawLine(position3, position0, Color.green, 1);
+                }
+	        }
+	    }
+	
+	
+	    public void SetContent(string content)
+	    {
+	        Content = content;
+	        text = content;
+	    }
+	
+	    public void ClearMatches()
+	    {
+	        AvailableSpriteMatches = new List<BaseMatch>();
+	        TruncateSpriteMatches = new List<BaseMatch>();
+	        UnderlineMatches = new List<BaseMatch>();
+	        SuperlinkMatches = new List<BaseMatch>();
+	        TransferSpaceMatches = new List<BaseMatch>();
+	        MatchesList = new List<List<BaseMatch>>();
+	        SuperlinkCallbackDictionary = new Dictionary<int, Action<int>>();
+	    }
+	
+	    private bool ParseMatches(ref string newText)
+	    {
+	        bool haveMatch = false;
+	        AvailableSpriteMatches = new List<BaseMatch>();
+	        UnderlineMatches = new List<BaseMatch>();
+	        SuperlinkMatches = new List<BaseMatch>();
+	        TransferSpaceMatches = new List<BaseMatch>();
+	        MatchesList = new List<List<BaseMatch>>
+	        {
+	            AvailableSpriteMatches,
+	            UnderlineMatches,
+	            SuperlinkMatches,
+	            TransferSpaceMatches,
+	        };
+	
+	        newText = ParseTransferSpaceMatches(newText);
+	        newText = ParseOpenCloseMatches(newText);
+	
+	        if (TransferSpaceMatches.Count > 0)
+	        {
+	            haveMatch = true;
+	        }
+	        if (AvailableSpriteMatches.Count > 0)
+	        {
+	            haveMatch = true;
+	        }
+	        if (UnderlineMatches.Count > 0)
+	        {
+	            haveMatch = true;
+	        }
+	        if (SuperlinkMatches.Count > 0)
+	        {
+	            haveMatch = true;
+	        }
+	        return haveMatch;
+	    }
+	
+	    private string ParseTransferSpaceMatches(string newText)
+	    {
+	        string pattern = "\\\\ ";
+	        Match match = Regex.Match(newText, pattern);
+	        while (match.Success)
+	        {
+	            MatchData matchData = new MatchData();
+	            matchData.OpenPatternIndex = match.Index;
+	            matchData.MatchLength = match.Length;
+	
+	            TransferSpaceMatch transferSpaceMatch = new TransferSpaceMatch();
+	            transferSpaceMatch.StartIndex = matchData.OpenPatternIndex;
+	            transferSpaceMatch.EndIndex = matchData.OpenPatternIndex;
+	            TransferSpaceMatches.Add(transferSpaceMatch);
+	
+	            newText = newText.Replace(match.Index, match.Length, TransferSpaceReplaceChar.ToString());
+	            match = Regex.Match(newText, pattern);
+	        }
+	        return newText;
+	    }
+	
+	    private string ParseOpenCloseMatches(string newText)
+	    {
+	        if (OpenPatterns.Count == 0 || ClosePatterns.Count == 0)
+	        {
+	            return newText;
+	        }
+	        if (OpenPatterns.Count != ClosePatterns.Count)
+	        {
+	            return newText;
+	        }
+	
+	        List<string> transferedOpenPatterns = new List<string>();
+	        foreach (var openPattern in OpenPatterns)
+	        {
+	            transferedOpenPatterns.Add(openPattern.GetTransferedPattern());
+	        }
+	        List<string> transferedClosePatterns = new List<string>();
+	        foreach (var closePattern in ClosePatterns)
+	        {
+	            transferedClosePatterns.Add(closePattern.GetTransferedPattern());
+	        }
+	        string orOpenPattern = transferedOpenPatterns.ToOrPattern();
+	        string orClosePattern = transferedClosePatterns.ToOrPattern();
+	        string regexPattern = string.Format("{0}.+{1}", orOpenPattern, orClosePattern);
+	
+	        Match match = Regex.Match(newText, regexPattern, RegexOptions.Singleline);
+	        while (match.Success)
+	        {
+	            Dictionary<string, int> openPatternIndexDictionary = new Dictionary<string, int>();
+	            Dictionary<string, int> closePatternIndexDictionary = new Dictionary<string, int>();
+	            for (int i = 0; i < OpenPatterns.Count; i++)
+	            {
+	                int index = match.Value.LastIndexOf(OpenPatterns[i]);
+	                if (index == -1) continue;
+	                else openPatternIndexDictionary.Add(OpenPatterns[i], index);
+	            }
+	            for (int i = 0; i < ClosePatterns.Count; i++)
+	            {
+	                string closeRegexPattern = ClosePatterns[i].GetTransferedPattern();
+	                Match closePatternMatch = Regex.Match(match.Value, closeRegexPattern);
+	                while (closePatternMatch.Success)
+	                {
+	                    if (openPatternIndexDictionary[OpenPatterns[i]] > closePatternMatch.Index)
+	                    {
+	                        closePatternMatch = closePatternMatch.NextMatch();
+	                    }
+	                    else
+	                    {
+	                        closePatternIndexDictionary.Add(ClosePatterns[i], closePatternMatch.Index);
+	                        break;
+	                    }
+	                }
+	            }
+	
+	            int[] openPatternIndexLength = new int[2];
+	            int[] closePatternIndexLength = new int[2];
+	            int dictionaryIndex = openPatternIndexDictionary.MyMax((pattern, index) => index);
+	            KVPair<string, int> pair = openPatternIndexDictionary.GetAtIndex(dictionaryIndex);
+	            openPatternIndexLength[0] = pair.value;
+	            openPatternIndexLength[1] = pair.key.Length;
+	            pair = closePatternIndexDictionary.GetAtIndex(dictionaryIndex);
+	            closePatternIndexLength[0] = pair.value;
+	            closePatternIndexLength[1] = pair.key.Length;
+	
+	            int openPatternIndex = openPatternIndexLength[0];
+	            int openPatternLength = openPatternIndexLength[1];
+	            int closePatternIndex = closePatternIndexLength[0];
+	            int closePatternLength = closePatternIndexLength[1];
+	            int contentIndex = openPatternIndex + openPatternLength;
+	            int contentLength = closePatternIndex - contentIndex;
+	            int matchLength = closePatternIndex + closePatternLength - openPatternIndex;
+	            string matchValue = match.Value.Substring(openPatternIndex, matchLength);
+	            string openPattern = match.Value.Substring(openPatternIndex, openPatternLength);
+	            string closePattern = match.Value.Substring(closePatternIndex, closePatternLength);
+	
+	            MatchData matchData = new MatchData();
+	            matchData.OpenPatternIndex = openPatternIndex + match.Index;
+	            matchData.OpenPatternLength = openPatternLength;
+	            matchData.ClosePatternIndex = closePatternIndex + match.Index;
+	            matchData.ClosePatternLength = closePatternLength;
+	            matchData.ContentIndex = contentIndex + match.Index;
+	            matchData.ContentLength = contentLength;
+	            matchData.MatchLength = matchLength;
+	            matchData.MatchValue = matchValue;
+	            matchData.OpenPattern = openPattern;
+	            matchData.ClosePattern = closePattern;
+	
+	            ParseResult parseResult = OpenCloseParseFunctionDictionary[openPattern].Invoke(newText, matchData);
+	            for (int i = 0; i < OpenPatterns.Count; i++)
+	            {
+	                if (matchData.OpenPattern != OpenPatterns[i]) continue;
+	                MatchesList[i].Add(parseResult.Match);
+	                for (int j = 0; j < MatchesList.Count; j++)
+	                {
+	                    AdjustIndicesByMatch(parseResult.ExtraOffset, parseResult.Match, MatchesList[j]);
+	                }
+	            }
+	            newText = parseResult.NewText;
+	            match = Regex.Match(newText, regexPattern);
+	        }
+	        return newText;
+	    }
+	
+	    private ParseResult ParseSpritePattern(string newText, MatchData matchData)
+	    {
+	        int id = 0;
+	        string name;
+	        string content = newText.Substring(matchData.ContentIndex, matchData.ContentLength);
+	        Match idMatch = Regex.Match(content, IDPattern);
+	        if (idMatch.Success)
+	        {
+	            Match match = Regex.Match(idMatch.Value, "\\d+");
+	            id = int.Parse(match.Value);
+	            name = content.Replace(idMatch.Index, idMatch.Length, "");
+	        }
+	        else
+	        {
+	            name = content;
+	        }
+	        newText = newText.Replace(matchData.OpenPatternIndex, matchData.MatchLength, SpriteReplaceChar.ToString());
+	
+	        SpriteMatch spriteMatch = new SpriteMatch();
+	        spriteMatch.ID = id;
+	        spriteMatch.Name = name;
+	        spriteMatch.SpriteSetting = SpriteSettingDictionary[id];
+	        spriteMatch.MatchData = matchData;
+	        spriteMatch.StartIndex = matchData.OpenPatternIndex;
+	        spriteMatch.EndIndex = matchData.OpenPatternIndex;
+	        ParseResult parseResult = new ParseResult();
+	        parseResult.Match = spriteMatch;
+	        parseResult.ExtraOffset = -matchData.ContentLength + 1;
+	        parseResult.NewText = newText;
+	        return parseResult;
+	    }
+	
+	    private ParseResult ParseUnderlinePattern(string newText, MatchData matchData)
+	    {
+	        newText = newText.Replace(matchData.ClosePatternIndex, matchData.ClosePatternLength, "");
+	        int id = 0;
+	        int extraOffset = 0;
+	        string content = newText.Substring(matchData.ContentIndex, matchData.ContentLength);
+	        Match idMatch = Regex.Match(content, IDPattern);
+	        if (idMatch.Success)
+	        {
+	            Match match = Regex.Match(idMatch.Value, "\\d+");
+	            id = int.Parse(match.Value);
+	            extraOffset = idMatch.Length;
+	            newText = newText.Replace(matchData.ContentIndex + idMatch.Index, idMatch.Length, "");
+	        }
+	        newText = newText.Replace(matchData.OpenPatternIndex, matchData.OpenPatternLength, "");
+	
+	        UnderlineMatch underlineMatch = new UnderlineMatch();
+	        underlineMatch.ID = id;
+	        underlineMatch.UnderlineSetting = UnderlineSettingDictionary[id];
+	        underlineMatch.MatchData = matchData;
+	        underlineMatch.StartIndex = matchData.OpenPatternIndex;
+	        underlineMatch.EndIndex = matchData.OpenPatternIndex + matchData.MatchLength - matchData.OpenPatternLength - matchData.ClosePatternLength - 1 - extraOffset;
+	        //print(newText);
+	        //print(underlineMatch.StartIndex + "  " + underlineMatch.EndIndex);
+	        ParseResult parseResult = new ParseResult();
+	        parseResult.Match = underlineMatch;
+	        parseResult.ExtraOffset = extraOffset;
+	        parseResult.NewText = newText;
+	        return parseResult;
+	    }
+	
+	    private ParseResult ParseSuperlinkPattern(string newText, MatchData matchData)
+	    {
+	        newText = newText.Replace(matchData.ClosePatternIndex, matchData.ClosePatternLength, "");
+	        int id = 0;
+	        int extraOffset = 0;
+	        string content = newText.Substring(matchData.ContentIndex, matchData.ContentLength);
+	        Match idMatch = Regex.Match(content, IDPattern);
+	        if (idMatch.Success)
+	        {
+	            Match match = Regex.Match(idMatch.Value, "\\d+");
+	            id = int.Parse(match.Value);
+	            extraOffset = idMatch.Length;
+	            newText = newText.Replace(matchData.ContentIndex + idMatch.Index, idMatch.Length, "");
+	        }
+	        newText = newText.Replace(matchData.OpenPatternIndex, matchData.OpenPatternLength, "");
+	
+	        SuperlinkMatch superlinkMatch = new SuperlinkMatch();
+	        superlinkMatch.ID = id;
+	        superlinkMatch.SuperlinkSetting = SuperlinkSettingDictionary[id];
+	        superlinkMatch.MatchData = matchData;
+	        superlinkMatch.StartIndex = matchData.OpenPatternIndex;
+	        superlinkMatch.EndIndex = matchData.OpenPatternIndex + matchData.MatchLength - matchData.OpenPatternLength - matchData.ClosePatternLength - 1 - extraOffset;
+	        ParseResult parseResult = new ParseResult();
+	        parseResult.Match = superlinkMatch;
+	        parseResult.ExtraOffset = extraOffset;
+	        parseResult.NewText = newText;
+	        return parseResult;
+	    }
+	
+	    private void AddNewOpenCloseMatch(string newText)
+	    {
+	        DelayCall.Call
+	        (
+	            1,
+	            () =>
+	            {
+	                text = newText;
+	                //ImageManager.Init(this);
+	                //ImageManager.ClearRenderTask();
+	                //ImageManager.RenderSprites(SpriteMatches);
+	                //ImageManager.RenderUnderlines(UnderlineMatches);
+	                //ImageManager.RenderSuperlinks(SuperlinkMatches);   
+	            }
+	        );
+	    }
+	
+	
+	    private void FillSpriteMatches(List<BaseMatch> spriteMatches, List<BaseMatch> truncateSpriteMatches, ref string newText/*, bool fillTruncate*/)
+	    {
+	        spriteMatches.MySort((match0, match1) => match1.StartIndex < match0.StartIndex);
+	
+	        float replaceCharWidth = GetCharacterWidth(SpriteReplaceChar);
+	        //bool isTextChanged = false;
+	        //if (!fillTruncate)
+	        //{
+	            /*isTextChanged = */ReplaceAllSpriteMatch(spriteMatches, replaceCharWidth, ref newText);
+	        //}
+	
+	        if (horizontalOverflow == HorizontalWrapMode.Overflow)
+	        {
+	            return;
+	        }
+	
+	        /*isTextChanged = isTextChanged | */CalculateSpriteMatchPosition(spriteMatches, truncateSpriteMatches, replaceCharWidth, ref newText);
+	        //return isTextChanged;
+	    }
+	
+	    private void ReplaceAllSpriteMatch(List<BaseMatch> spriteMatches, float replaceCharWidth, ref string newText)
+	    {
+	        foreach (var match in spriteMatches)
+	        {
+	            SpriteMatch spriteMatch = match as SpriteMatch;
+	            SpriteInfo spriteInfo = SpriteInfoManager.GetSpriteInfo(spriteMatch.Name);
+	            float scaledWidth = replaceCharWidth * 2 * spriteMatch.SpriteSetting.Scale;
+	            float minReplaceWidth = scaledWidth;
+	            if (KerningSolution == KerningSolution.Precalculate)
+	            {
+	                int frontIndex = spriteMatch.StartIndex - 1;
+	                int rearIndex = spriteMatch.EndIndex + 1;
+	                float frontKerning;
+	                float rearKerning;
+	                if (frontIndex < 0)
+	                {
+	                    frontKerning = 0;
+	                }
+	                else
+	                {
+	                    frontKerning = GetKerning(text[frontIndex], SpriteReplaceChar);
+	                }
+	                if (rearIndex >= text.Length)
+	                {
+	                    rearKerning = 0;
+	                }
+	                else
+	                {
+	                    rearKerning = GetKerning(SpriteReplaceChar, text[rearIndex]);
+	                }
+	                if (frontKerning < 0)
+	                {
+	                    minReplaceWidth -= frontKerning;
+	                }
+	                if (rearKerning < 0)
+	                {
+	                    minReplaceWidth -= rearKerning;
+	                }
+	                spriteMatch.FrontKerning = frontKerning;
+	                spriteMatch.RearKerning = rearKerning;
+	            }
+	            int replaceCharCount = Mathf.CeilToInt(minReplaceWidth / replaceCharWidth);
+	            
+	            for (int j = 0; j < MatchesList.Count; j++)
+	            {
+	                AdjustIndicesByIndex(replaceCharCount - 1, spriteMatch, MatchesList[j]);
+	            }
+	
+	            StringBuilder stringBuilder = new StringBuilder();
+	            for (int j = 0; j < replaceCharCount; j++)
+	            {
+	                stringBuilder.Append(SpriteReplaceChar);
+	            }
+	            newText = newText.Replace(spriteMatch.StartIndex, spriteMatch.Length, stringBuilder.ToString());
+	
+	            spriteMatch.SpriteInfo = spriteInfo;
+	            spriteMatch.ReplaceWidth = replaceCharCount*replaceCharWidth;
+	            spriteMatch.ScaledWidth = scaledWidth;
+	            spriteMatch.EndIndex += replaceCharCount - 1;
+	        }
+	    }
+	
+	    private void CalculateSpriteMatchPosition(List<BaseMatch> spriteMatches, List<BaseMatch> stashSpriteMatches, float replaceCharWidth, ref string newText/*, bool fillTruncate*/)
+	    {
+	        float changeLineAccuracy = replaceCharWidth/5;
+	        //bool isTextChanged = false;
+	        TextGenerationSettings settings = GetGenerationSettings(rectTransform.rect.size);
+	        settings.textAnchor = alignment;
+	        CalculateTextGenerator = new TextGenerator();
+	        CalculateTextGenerator.Populate(newText, settings);
+	        AdjustVerts(CalculateTextGenerator.verts);
+	        //if (IsSpritesOutOfBound(spriteMatches))
+	        //{
+	        //    UnityEngine.Debug.LogWarning("文本框过小");
+	        //    if (!fillTruncate)
+	        //    {
+	        //        StashSpriteMatch(0, spriteMatches, stashSpriteMatches);
+	        //    }
+	        //    return false;
+	        //}
+	        for (int i = 0; i < spriteMatches.Count; i++)
+	        {
+	            SpriteMatch spriteMatch = spriteMatches[i] as SpriteMatch;
+	            //print(CalculateTextGenerator.lineCount);
+	            int lineIndex = GetLineIndex(CalculateTextGenerator, spriteMatch.StartIndex);
+	            int lineStartCharacterIndex;
+	            int lineEndCharacterIndex;
+	            if (lineIndex != -1)
+	            {
+	                lineStartCharacterIndex = CalculateTextGenerator.lines[lineIndex].startCharIdx;
+	                lineEndCharacterIndex = GetLineEndCharacterIndex(CalculateTextGenerator, lineIndex, newText.Length);
+	            }
+	            else
+	            {
+	                lineStartCharacterIndex = -1;
+	                lineEndCharacterIndex = -1;
+	            }
+	            //int lineCharacterCount = lineEndCharacterIndex - lineStartCharacterIndex;
+	            //print(spriteMatch.EndIndex);
+	            //print(lineEndCharacterIndex);
+	            if (spriteMatch.EndIndex > lineEndCharacterIndex) //被Truncate
+	            {
+	                //if (!fillTruncate)
+	                //{
+	                    StashSpriteMatch(i, spriteMatches, stashSpriteMatches);
+	                //}
+	                break;
+	            }
+	            List<BaseMatch> matches = GetMatchesInLine(spriteMatches, CalculateTextGenerator, lineIndex, lineEndCharacterIndex);
+	            if (matches.Count == 1)
+	            {
+	                continue;
+	            }
+	            else if (matches.Count > 1)
+	            {
+	                bool changeLine = false;
+	                if (spriteMatch.ReplaceWidth > rectTransform.rect.width) //图片太大 需要换行
+	                {
+	                    if (i < spriteMatches.Count - 1)
+	                    {
+	                        i++;
+	                        spriteMatch = spriteMatches[i] as SpriteMatch;
+	                        changeLine = true;
+	                        //isTextChanged = true;
+	                    }
+	                }
+	                else
+	                {
+	                    float leftMargin = rectTransform.rect.xMin - CalculateTextGenerator.verts[0 + lineStartCharacterIndex * 4].position.x;
+	                    float rightMargin = CalculateTextGenerator.verts[1 + lineEndCharacterIndex * 4].position.x - rectTransform.rect.xMax;
+	                    float horizontalMargin = CalculateTextGenerator.verts[1 + lineEndCharacterIndex * 4].position.x - CalculateTextGenerator.verts[0 + lineStartCharacterIndex * 4].position.x - rectTransform.rect.width;
+	                    if (alignment == TextAnchor.LowerLeft || alignment == TextAnchor.MiddleLeft || alignment == TextAnchor.UpperLeft)
+	                    {
+	                        if (leftMargin <= 0 && rightMargin > changeLineAccuracy)
+	                        {
+	                            //向右突出
+	                            matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	                            float margin = horizontalMargin;
+	                            spriteMatch = GetMarginSpriteMatch(matches, margin, replaceCharWidth) as SpriteMatch;
+	                            i = spriteMatches.IndexOf(spriteMatch);
+	                            changeLine = true;
+	                            //isTextChanged = true;
+	                        }
+	                    }
+	                    else if (alignment == TextAnchor.LowerCenter || alignment == TextAnchor.MiddleCenter || alignment == TextAnchor.UpperCenter)
+	                    {
+	                        if (leftMargin > changeLineAccuracy && rightMargin > changeLineAccuracy)
+	                        {
+	                            //左右同时突出
+	                            matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	                            float margin = leftMargin + rightMargin;
+	                            spriteMatch = GetMarginSpriteMatch(matches, margin, replaceCharWidth) as SpriteMatch;
+	                            i = spriteMatches.IndexOf(spriteMatch);
+	                            changeLine = true;
+	                            //isTextChanged = true;
+	                        }
+	                        else if (leftMargin <= 0 && rightMargin > changeLineAccuracy)
+	                        {
+	                            //向右突出
+	                            matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	                            float margin = horizontalMargin;
+	                            spriteMatch = GetMarginSpriteMatch(matches, margin, replaceCharWidth) as SpriteMatch;
+	                            i = spriteMatches.IndexOf(spriteMatch);
+	                            changeLine = true;
+	                            //isTextChanged = true;
+	                        }
+	                    }
+	                    else if (alignment == TextAnchor.LowerRight || alignment == TextAnchor.MiddleRight || alignment == TextAnchor.UpperRight)
+	                    {
+	                        if (leftMargin > changeLineAccuracy && rightMargin <= 0)
+	                        {
+	                            //向左突出
+	                            matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	                            float margin = leftMargin;
+	                            spriteMatch = GetMarginSpriteMatch(matches, margin, replaceCharWidth) as SpriteMatch;
+	                            i = spriteMatches.IndexOf(spriteMatch);
+	                            changeLine = true;
+	                            //isTextChanged = true;
+	                        }
+	                        else if (leftMargin <= 0 && rightMargin > changeLineAccuracy)
+	                        {
+	                            //向右突出
+	                            matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	                            float margin = horizontalMargin;
+	                            spriteMatch = GetMarginSpriteMatch(matches, margin, replaceCharWidth) as SpriteMatch;
+	                            i = spriteMatches.IndexOf(spriteMatch);
+	                            changeLine = true;
+	                            //isTextChanged = true;
+	                        }
+	                    }
+	                }
+	                if (changeLine)
+	                {
+	                    lineIndex++;
+	                    if (lineIndex < GetMaxLineCount()) //换行后不被Truncate
+	                    {
+	                        newText = newText.Insert(spriteMatch.StartIndex, "\n");
+	                        for (int j = 0; j < MatchesList.Count; j++)
+	                        {
+	                            AdjustIndicesByIndex(1, spriteMatch, MatchesList[j]);
+	                        }
+	                        spriteMatch.StartIndex += 1;
+	                        spriteMatch.EndIndex += 1;
+	
+	                        CalculateTextGenerator = new TextGenerator();
+	                        CalculateTextGenerator.Populate(newText, settings);
+	                        AdjustVerts(CalculateTextGenerator.verts);
+	                    }
+	                }
+	            }
+	            //if (fillTruncate)
+	            //{
+	            //    stashSpriteMatches.Add(spriteMatch);
+	            //    spriteMatches.Remove(spriteMatch);
+	            //}
+	        }
+	        //return isTextChanged;
+	    }
+	
+	    //private bool IsSpritesOutOfBound(List<BaseMatch> spriteMatches)
+	    //{
+	    //    for (int i = 0; i < spriteMatches.Count; i++)
+	    //    {
+	    //        SpriteMatch spriteMatch = spriteMatches[i] as SpriteMatch;
+	    //        if (spriteMatch.ReplaceWidth > rectTransform.rect.width)
+	    //        {
+	    //            return true;
+	    //        }
+	    //    }
+	    //    return false;
+	    //}
+	
+	    private void StashSpriteMatch(int startIndex, List<BaseMatch> spriteMatches, List<BaseMatch> stashSpriteMatches)
+	    {
+	        for (int i = startIndex; i < spriteMatches.Count; i++)
+	        {
+	            stashSpriteMatches.Add(spriteMatches[i]);
+	            spriteMatches.Remove(spriteMatches[i]);
+	            i--;
+	        }
+	    }
+	
+	    private List<BaseMatch> GetMatchesInLine(List<BaseMatch> matches, TextGenerator textGenerator, int lineIndex, int endCharacterIndex)
+	    {
+	        List<BaseMatch> result = new List<BaseMatch>();
+	        foreach (var match in matches)
+	        {
+	            if (match.StartIndex > endCharacterIndex)
+	            {
+	                continue;
+	            }
+	            int matchLineIndex = GetLineIndex(textGenerator, match.StartIndex);
+	            if (matchLineIndex == lineIndex)
+	            {
+	                result.Add(match);
+	            }
+	        }
+	        return result;
+	    }
+	
+	    private BaseMatch GetMarginSpriteMatch(List<BaseMatch> matches, float margin, float replaceCharWidth)
+	    {
+	        foreach (var match in matches)
+	        {
+	            margin -= replaceCharWidth * match.Length;
+	            if (margin <= 0)
+	            {
+	                return match;
+	            }
+	        }
+	        throw new Exception();
+	    }
+	
+	    //private bool IsCharacterTruncated(TextGenerator textGenerator, int characterIndex)
+	    //{
+	    //    print(textGenerator.characterCount - 1);
+	    //    print(characterIndex + 1);
+	    //    if (textGenerator.characterCount - 1 < characterIndex + 1)
+	    //    {
+	    //        return true;
+	    //    }
+	    //    else
+	    //    {
+	    //        return false;
+	    //    }
+	    //}
+	
+	    private void HandleTransferSpaceMatch(VertexHelper toFill, List<UIVertex> vertices)
+	    {
+	        foreach (var transferSpaceMatch in TransferSpaceMatches)
+	        {
+	            int index0 = 0 + transferSpaceMatch.StartIndex * 4;
+	            int index1 = 1 + transferSpaceMatch.EndIndex * 4;
+	            int index2 = 2 + transferSpaceMatch.EndIndex * 4;
+	            int index3 = 3 + transferSpaceMatch.StartIndex * 4;
+	            UIVertex vertex0 = vertices[index0];
+	            UIVertex vertex1 = vertices[index1];
+	            UIVertex vertex2 = vertices[index2];
+	            UIVertex vertex3 = vertices[index3];
+	            vertex0.color = new Color32(0, 0, 0, 0);
+	            vertex1.color = new Color32(0, 0, 0, 0);
+	            vertex2.color = new Color32(0, 0, 0, 0);
+	            vertex3.color = new Color32(0, 0, 0, 0);
+	            vertices[index0] = vertex0;
+	            vertices[index1] = vertex1;
+	            vertices[index2] = vertex2;
+	            vertices[index3] = vertex3;
+	            toFill.SetUIVertex(vertex0, index0);
+	            toFill.SetUIVertex(vertex1, index1);
+	            toFill.SetUIVertex(vertex2, index2);
+	            toFill.SetUIVertex(vertex3, index3);
+	        }
+	    }
+	
+	
+	    public string GetUnparsedText()
+	    {
+	        List<BaseMatch> matches = new List<BaseMatch>();
+	        matches.AddRange(TransferSpaceMatches);
+	        matches.AddRange(AvailableSpriteMatches);
+	        matches.AddRange(UnderlineMatches);
+	        matches.AddRange(SuperlinkMatches);
+	        matches.MySort((match0, match1) => match1.StartIndex > match0.StartIndex);
+	        string result = text;
+	        foreach (var baseMatch in matches)
+	        {
+	            if (baseMatch is TransferSpaceMatch)
+	            {
+	                result = result.Insert(baseMatch.StartIndex, "\\");
+	            }
+	            else
+	            {
+	                string openPattern = baseMatch.MatchData.OpenPattern;
+	                string idPattern = IDPattern.Replace("\\d+", baseMatch.ID.ToString());
+	                idPattern = idPattern.Replace("\\", "");
+	                string closePattern = baseMatch.MatchData.ClosePattern;
+	                result = result.Insert(baseMatch.StartIndex + baseMatch.EndIndex, idPattern + closePattern);
+	                result = result.Insert(baseMatch.StartIndex, openPattern);
+	            }
+	        }
+	        return result;
+	    }
+	
+	
+	    private Vector3[] GetLocalCorners()
+	    {
+	        Vector3[] corners = new Vector3[4];
+	        rectTransform.GetLocalCorners(corners);
+	        return corners;
+	    }
+	
+	    private Vector3[] GetWorldCorners()
+	    {
+	        Vector3[] corners = new Vector3[4];
+	        rectTransform.GetWorldCorners(corners);
+	        return corners;
+	    }
+	
+	    private float GetKerning(char c0, char c1)
+	    {
+	        TextGenerator textGenerator = new TextGenerator(0);
+	        textGenerator.Populate(string.Format("{0}{1}", c0, c1), GetGenerationSettings(rectTransform.rect.size));
+	        //AdjustVerts(textGenerator.verts);
+	        float kerning = textGenerator.verts[4].position.x - textGenerator.verts[1].position.x;
+	        kerning /= canvas.scaleFactor;
+	        return kerning;
+	        //return textGenerator.verts[4].position.x - textGenerator.verts[1].position.x;
+	    }
+	
+	    private float GetCharacterHeight(char c)
+	    {
+	        TextGenerator textGenerator = new TextGenerator(0);
+	        textGenerator.Populate(c.ToString(), GetGenerationSettings(rectTransform.rect.size));
+	        //AdjustVerts(textGenerator.verts);
+	        float height = textGenerator.verts[0].position.y - textGenerator.verts[2].position.y;
+	        height /= canvas.scaleFactor;
+	        return height;
+	        //return textGenerator.verts[0].position.y - textGenerator.verts[2].position.y;
+	    }
+	
+	    private float GetCharacterWidth(char c)
+	    {
+	        TextGenerator textGenerator = new TextGenerator(0);
+	        textGenerator.Populate(c.ToString(), GetGenerationSettings(rectTransform.rect.size));
+	        //AdjustVerts(textGenerator.verts);
+	        float width = textGenerator.verts[1].position.x - textGenerator.verts[0].position.x;
+	        width /= canvas.scaleFactor;
+	        return width;
+	        //return textGenerator.verts[1].position.x - textGenerator.verts[0].position.x;
+	    }
+	
+	    public float GetPenPositionY(int lineIndex, int lineCount)
+	    {
+	        StringBuilder stringBuilder = new StringBuilder();
+	        for (int i = 0; i < lineCount; i++)
+	        {
+	            stringBuilder.Append(CalculateReferenceChar);
+	            if (i < lineCount - 1)
+	            {
+	                stringBuilder.Append("\n");
+	            }
+	        }
+	        TextGenerator textGenerator = new TextGenerator();
+	        textGenerator.Populate(stringBuilder.ToString(), GetGenerationSettings(rectTransform.rect.size));
+	        AdjustVerts(textGenerator.verts);
+	        return textGenerator.verts[2 + lineIndex*8].position.y;
+	    }
+	
+	    public float GetLineCenterY(int lineIndex, int lineCount)
+	    {
+	        StringBuilder stringBuilder = new StringBuilder();
+	        for (int i = 0; i < lineCount; i++)
+	        {
+	            stringBuilder.Append(CalculateReferenceChar);
+	            if (i < lineCount - 1)
+	            {
+	                stringBuilder.Append("\n");
+	            }
+	        }
+	        TextGenerator textGenerator = new TextGenerator();
+	        textGenerator.Populate(stringBuilder.ToString(), GetGenerationSettings(rectTransform.rect.size));
+	        AdjustVerts(textGenerator.verts);
+	        return (textGenerator.verts[0 + lineIndex*8].position.y + textGenerator.verts[2 + lineIndex*8].position.y)/2;
+	    }
+	
+	    public int GetLineIndex(TextGenerator textGenerator, int charIndex)
+	    {
+	        for (int i = 0; i < textGenerator.lines.Count; i++)
+	        {
+	            if (charIndex >= textGenerator.lines.Back(i).startCharIdx)
+	            {
+	                return textGenerator.lines.Count - 1 - i;
+	            }
+	        }
+	        return -1;
+	    }
+	
+	    public int GetMaxLineCount()
+	    {
+	        TextGenerationSettings settings = GetGenerationSettings(rectTransform.rect.size);
+	        settings.textAnchor = alignment;
+	        CalculateTextGenerator = new TextGenerator();
+	        int lineCount = 0;
+	        StringBuilder stringBuilder = new StringBuilder();
+	        while (CalculateTextGenerator.GetPreferredHeight(stringBuilder.ToString(), settings) / canvas.scaleFactor < rectTransform.rect.height)
+	        {
+	            lineCount++;
+	            stringBuilder.Append("\n");
+	        }
+	        return lineCount;
+	    }
+	
+	    public int GetLineEndCharacterIndex(TextGenerator textGenerator, int lineIndex, int realCharacterCount)
+	    {
+	        if (lineIndex >= textGenerator.lines.Count)
+	        {
+	            throw new Exception();
+	        }
+	
+	        if (textGenerator.lines.Count == 1 || lineIndex == textGenerator.lines.Count - 1)
+	        {
+	            if (textGenerator.characterCount - 1 == realCharacterCount) //TextGenerator会自动在结尾添加换行符
+	            {
+	                return realCharacterCount - 1;
+	            }
+	            else
+	            {
+	                return textGenerator.characterCount - 1;
+	            }
+	        }
+	        else
+	        {
+	            return textGenerator.lines[lineIndex + 1].startCharIdx - 1;
+	        }
+	    }
+	
+	    public int GetLineCharacterCount(TextGenerator textGenerator, int lineIndex)
+	    {
+	        if (lineIndex >= textGenerator.lines.Count)
+	        {
+	            throw new Exception();
+	        }
+	
+	        if (textGenerator.lines.Count == 1)
+	        {
+	            return textGenerator.characterCount;
+	        }
+	
+	        if (lineIndex == textGenerator.lines.Count - 1)
+	        {
+	            return textGenerator.characterCount - textGenerator.lines.Back(0).startCharIdx;
+	        }
+	        else
+	        {
+	            return textGenerator.lines[lineIndex + 1].startCharIdx - textGenerator.lines[lineIndex].startCharIdx;
+	        }
+	    }
+	
+	    public float GetLineMaxY(TextGenerator textGenerator, int lineIndex)
+	    {
+	        float lineCenterY = GetLineCenterY(lineIndex, textGenerator.lineCount);
+	        float lineHeight = textGenerator.lines[lineIndex].height;
+	        return lineCenterY + lineHeight/2;
+	    }
+	
+	    public float GetLineMinY(TextGenerator textGenerator, int lineIndex)
+	    {
+	        float lineCenterY = GetLineCenterY(lineIndex, textGenerator.lineCount);
+	        float lineHeight = textGenerator.lines[lineIndex].height;
+	        return lineCenterY - lineHeight/2;
+	    }
+	
+	    public float GetLineTopY(TextGenerator textGenerator, int lineIndex)
+	    {
+	        return textGenerator.lines[lineIndex].topY;
+	    }
+	
+	    public float GetLineBottomY(TextGenerator textGenerator, int lineIndex)
+	    {
+	        return textGenerator.lines[lineIndex].topY - textGenerator.lines[lineIndex].height;
+	    }
+	
+	    public bool IsSameLine(TextGenerator textGenerator, int charIndex0, int charIndex1)
+	    {
+	        if (textGenerator.lines.Count == 1)
+	        {
+	            return true;
+	        }
+	        else
+	        {
+	            for (int i = 0; i < textGenerator.lines.Count - 1; i++)
+	            {
+	                if ((charIndex0 >= textGenerator.lines[i].startCharIdx && charIndex0 < textGenerator.lines[i + 1].startCharIdx) && charIndex1 >= textGenerator.lines[i + 1].startCharIdx)
+	                {
+	                    return false;
+	                }
+	            }
+	            return true;
+	        }
+	    }
+	
+	
+	    private void AdjustVerts(IList<UIVertex> verts) //取自UGUI源代码
+	    {
+	        float unitsPerPixel = 1/pixelsPerUnit;
+	        int vertCount = verts.Count - 4;
+	
+	        Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y)*unitsPerPixel;
+	        roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
+	        if (roundingOffset != Vector2.zero)
+	        {
+	            for (int i = 0; i < vertCount; ++i)
+	            {
+	                UIVertex vertex = verts[i];
+	                vertex.position *= unitsPerPixel;
+	                vertex.position.x += roundingOffset.x;
+	                vertex.position.y += roundingOffset.y;
+	                verts[i] = vertex;
+	            }
+	        }
+	        else
+	        {
+	            for (int i = 0; i < vertCount; ++i)
+	            {
+	                UIVertex vertex = verts[i];
+	                vertex.position *= unitsPerPixel;
+	                verts[i] = vertex;
+	            }
+	        }
+	    }
+	
+	    private void AdjustIndicesByIndex(int indexOffset, BaseMatch referenceMatch, List<BaseMatch> adjusteeMatches)
+	    {
+	        for (int i = 0; i < adjusteeMatches.Count; i++)
+	        {
+	            BaseMatch adjusteeMatch = adjusteeMatches[i];
+	            if (referenceMatch == adjusteeMatch) continue;
+	            if (referenceMatch.StartIndex < adjusteeMatch.StartIndex) //referenceIndex-adjusteeMatch-adjusteeMatch
+	            {
+	                adjusteeMatch.StartIndex += indexOffset; 
+	                adjusteeMatch.EndIndex += indexOffset;
+	            }
+	            else if (referenceMatch.StartIndex < adjusteeMatch.EndIndex) //adjusteeMatch-referenceIndex-adjusteeMatch
+	            {
+	                adjusteeMatch.EndIndex += indexOffset;
+	            }
+	        }
+	    }
+	
+	    private void AdjustIndicesByMatch(int extraOffset, BaseMatch referenceMatch, List<BaseMatch> adjusteeMatches)
+	    {
+	        for (int i = 0; i < adjusteeMatches.Count; i++)
+	        {
+	            BaseMatch adjusteeMatch = adjusteeMatches[i];
+	            if (referenceMatch == adjusteeMatch) continue;
+	            if (referenceMatch.StartIndex < adjusteeMatch.EndIndex) //排除 adjusteeMatch...adjusteeMatch...refeMatch-refeMatch
+	            {
+	                if (referenceMatch.EndIndex < adjusteeMatch.StartIndex) //refeMatch-refeMatch...adjusteeMatch-adjusteeMatch 
+	                {
+	                    int indexOffset = -referenceMatch.MatchData.OpenPatternLength - referenceMatch.MatchData.ClosePatternLength + extraOffset;
+	                    adjusteeMatch.StartIndex += indexOffset;
+	                    adjusteeMatch.EndIndex += indexOffset;
+	                }
+	                else if (referenceMatch.EndIndex < adjusteeMatch.EndIndex) //refeMatch...adjusteeMatch...refeMatch...adjusteeMatch
+	                {
+	                    int indexOffset = -referenceMatch.MatchData.ClosePatternLength + extraOffset;
+	                    adjusteeMatch.StartIndex += indexOffset;
+	                    adjusteeMatch.EndIndex += indexOffset;
+	                }
+	                else if (referenceMatch.EndIndex > adjusteeMatch.EndIndex) //adjusteeMatch...refeMatch...adjusteeMatch...refeMatch
+	                {
+	                    int indexOffset = -referenceMatch.MatchData.OpenPatternLength + extraOffset;
+	                    adjusteeMatch.StartIndex += indexOffset;
+	                    adjusteeMatch.EndIndex += indexOffset;
+	                }
+	                //不应该出现的情况refeMatch...adjusteeMatch-adjusteeMatch...refeMatch    adjusteeMatch...refeMatch...refeMatch...adjusteeMatch
+	            }
+	        }
+	    }
+	
+	
+	    public void GetSegments<T>(string newText, TextGenerator textGenerator, T match, List<char> excludeChars) where T : SegmentMatch
+	    {
+	        //print(excludeChars.Count);
+	        if (textGenerator.lines.Count == 1)
+	        {
+	            match.Segments.Add(new List<int> {match.StartIndex, match.EndIndex});
+	        }
+	        else
+	        {
+	            int endLineIndex;
+	            int startLineIndex;
+	            for (int i = 0; i < textGenerator.lines.Count; i++)
+	            {
+	                startLineIndex = textGenerator.lines.Count - 1 - i;
+	                if (match.StartIndex >= textGenerator.lines[startLineIndex].startCharIdx)
+	                {
+	                    if (startLineIndex == textGenerator.lines.Count - 1)
+	                    {
+	                        match.Segments.Add(new List<int> {match.StartIndex, match.EndIndex});
+	                        goto jumpOut;
+	                    }
+	                    else
+	                    {
+	                        if (match.EndIndex >= textGenerator.lines.Back(0).startCharIdx)
+	                        {
+	                            endLineIndex = textGenerator.lines.Count - 1;
+	                        }
+	                        else
+	                        {
+	                            for (int j = startLineIndex; j < textGenerator.lines.Count; j++)
+	                            {
+	                                if (match.EndIndex < textGenerator.lines[j + 1].startCharIdx)
+	                                {
+	                                    if (j == startLineIndex)
+	                                    {
+	                                        match.Segments.Add(new List<int> {match.StartIndex, match.EndIndex});
+	                                        goto jumpOut;
+	                                    }
+	                                    else
+	                                    {
+	                                        endLineIndex = j;
+	                                        goto segment;
+	                                    }
+	                                }
+	                            }
+	                            throw new Exception();
+	                        }
+	                        segment:
+	                        int segmentCount = endLineIndex - startLineIndex + 1;
+	                        match.Segments.Add(new List<int> {match.StartIndex, textGenerator.lines[startLineIndex + 1].startCharIdx - 1}); //第一段
+	                        match.Segments.Add(new List<int> {textGenerator.lines[endLineIndex].startCharIdx, match.EndIndex}); //最后一段
+	                        for (int k = 0; k < segmentCount - 2; k++)
+	                        {
+	                            match.Segments.Add(new List<int> {textGenerator.lines[startLineIndex + 1 + k].startCharIdx, textGenerator.lines[startLineIndex + 2 + k].startCharIdx - 1});
+	                        }
+	                        goto jumpOut;
+	                    }
+	                }
+	            }
+	        }
+	        jumpOut:
+	
+	        //for (int i = 0; i < match.Segments.Count; i++)
+	        //{
+	        //    Debug.LogWarning(match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	        //}
+	
+	        exclude:
+	        for (int i = 0; i < match.Segments.Count; i++)
+	        {
+	            int startIndex = match.Segments[i][0];
+	            int endIndex = match.Segments[i][1];
+	            for (int j = startIndex; j <= endIndex; j++)
+	            {
+	                //print((int)newText[j]);
+	                if (excludeChars.Contains(newText[j]))
+	                {
+	                    if (j == startIndex)
+	                    {
+	                        //Debug.Log("start " + match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	                        //Debug.Log(j);
+	                        //newText = newText.Remove(startIndex, 1);
+	                        match.Segments[i][0] = startIndex + 1;
+	                        //Debug.Log("start " + match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	                    }
+	                    else if (j == endIndex)
+	                    {
+	                        //Debug.Log("end " + match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	                        //Debug.Log(j);
+	                        //newText = newText.Remove(endIndex, 1);
+	                        match.Segments[i][1] = endIndex - 1;
+	                        //Debug.Log("end " + match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	                    }
+	                    else
+	                    {
+	                        //Debug.Log(j);
+	                        //newText = newText.Remove(j, 1);
+	                        //Debug.Log("remove " + match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	                        match.Segments.RemoveAt(i);
+	                        match.Segments.Insert(0, new List<int> {startIndex, j - 1});
+	                        //Debug.Log("add " + match.Segments[0][0] + "\t" + match.Segments[0][1]);
+	                        match.Segments.Insert(0, new List<int> {j + 1, endIndex});
+	                        //Debug.Log("add " + match.Segments[0][0] + "\t" + match.Segments[0][1]);
+	                    }
+	                    goto exclude;
+	                }
+	            }
+	        }
+	
+	        for (int i = 0; i < match.Segments.Count; i++)
+	        {
+	            if (match.Segments[i][1] < match.Segments[i][0])
+	            {
+	                match.Segments.RemoveAt(i--);
+	            }
+	        }
+	
+	        //for (int i = 0; i < match.Segments.Count; i++)
+	        //{
+	        //    Debug.Log(match.Segments[i][0] + "\t" + match.Segments[i][1]);
+	        //}
+	    }
+	
+	
+	    private int GetVerticalBestfitFontSize(string newText)
+	    {
+	        int antiCrush = 0;
+	        TextGenerationSettings settings = GetGenerationSettings(rectTransform.rect.size);
+	        List<int> leftSizes = new List<int> {resizeTextMinSize};
+	        List<int> rightSizes = new List<int> {resizeTextMaxSize};
+	        settings.fontSize = (leftSizes[0] + rightSizes[0])/2;
+	        while (true)
+	        {
+	            if (antiCrush++ > 300)
+	            {
+	                throw new Exception("Crush");
+	            }
+	
+	            float preferredHeight = cachedTextGenerator.GetPreferredHeight(newText, settings);
+	            if (preferredHeight > rectTransform.rect.size.y*canvas.scaleFactor)
+	            {
+	                rightSizes.Add(settings.fontSize);
+	                settings.fontSize = (leftSizes[leftSizes.Count - 1] + settings.fontSize)/2;
+	            }
+	            else if (preferredHeight < rectTransform.rect.size.y*canvas.scaleFactor)
+	            {
+	                if (leftSizes.Contains(settings.fontSize))
+	                {
+	                    return settings.fontSize;
+	                }
+	                else
+	                {
+	                    leftSizes.Add(settings.fontSize);
+	                }
+	
+	                settings.fontSize = (rightSizes[rightSizes.Count - 1] + settings.fontSize)/2;
+	            }
+	            else
+	            {
+	                return settings.fontSize;
+	            }
+	        }
+	    }
+	
+	    public void ApplyVerticalBestfitSize()
+	    {
+	        resizeTextMinSize = MinSize;
+	        resizeTextMaxSize = MaxSize;
+	    }
+	
+	    public void DisableOriginBestfit()
+	    {
+	        resizeTextForBestFit = false;
+	    }
+	
+	    public void EnableVerticalBestfit()
+	    {
+	        UseOriginBestfit = resizeTextForBestFit;
+	        MinSize = resizeTextMinSize;
+	        MaxSize = resizeTextMaxSize;
+	        UseVerticalBestfit = true;
+	        VerticalBestfitDirty = true;
+	        DisableOriginBestfit();
+	    }
+	
+	    public void DisableVerticalBestfit()
+	    {
+	        resizeTextForBestFit = UseOriginBestfit;
+	        UseVerticalBestfit = false;
+	    }
+	
+	
+	    private bool ParseAndFillMatches(ref string newText)
+	    {
+	        bool haveMatch = ParseMatches(ref newText);
+	        if (haveMatch)
+	        {
+	            FillSpriteMatches(AvailableSpriteMatches, TruncateSpriteMatches, ref newText);
+	        }
+	        return haveMatch;
+	    }
+	
+	    public void EnableDebugMode()
+	    {
+	        //TextGenerationSettings settings = GetGenerationSettings(rectTransform.rect.size);
+	        //settings.textAnchor = alignment;
+	        //CalculateTextGenerator = new TextGenerator();
+	        //print(CalculateTextGenerator.GetPreferredHeight("", settings) / canvas.scaleFactor);
+	
+	        //print(cachedTextGenerator.characterCount);
+	        //print(cachedTextGenerator.lineCount);
+	
+	        Debug = true;
+	        Content = text;
+	        SetVerticesDirty();
+	        SetLayoutDirty();
+	        //string newText = Content;
+	        //if (ParseAndFillMatches(ref newText))
+	        //{
+	        //    AddNewOpenCloseMatch(newText);
+	        //}
+	    }
+	
+	    public void DisableDebugMode()
+	    {
+	        Debug = false;
+	        text = Content;
+	        ClearMatches();
+	        ImageManager.ClearRenderTask();
+	    }
+
+
+
+        public void OnPointerClick(PointerEventData eventData)
+        {
+            //Vector3 clickPosition = eventData.position;
+            //Debug.Log(clickPosition);
+            OnPointerClick(eventData.position);
+        }
+
+	    private Dictionary<int, List<List<Vector3>>> SuperlinkPositionsDictionary = new Dictionary<int, List<List<Vector3>>>();
+        public void OnPointerClick(Vector3 worldPosition)
+        {
+            SuperlinkPositionsDictionary = new Dictionary<int, List<List<Vector3>>>();
+            foreach (var match in SuperlinkMatches)
+            {
+                SuperlinkMatch superlinkMatch = (SuperlinkMatch) match;
+                SuperlinkPositionsDictionary.Add(superlinkMatch.ID, superlinkMatch.RectanglePositionsList);
+            }
+
+            Vector3 clickPosition = worldPosition;
+            //Vector3 clickPosition = rectTransform.InverseTransformPoint(worldPosition);
+            foreach (var kv in SuperlinkPositionsDictionary)
+            {
+                foreach (var positions in kv.Value)
+                {
+                    float minX = positions.MyMin(vector => vector.x).x;
+                    float maxX = positions.MyMax(vector => vector.x).x;
+                    float minY = positions.MyMin(vector => vector.y).y;
+                    float maxY = positions.MyMax(vector => vector.y).y;
+                    //UnityEngine.Debug.Log(clickPosition);
+                    //UnityEngine.Debug.Log(minX + "  " + maxX + "  " + minY + "  " + maxY);
+                    if ((minX <= clickPosition.x && clickPosition.x <= maxX) && (minY <= clickPosition.y && clickPosition.y <= maxY))
+                    {
+                        if (SuperlinkCallbackDictionary.ContainsKey(kv.Key))
+                        {
+                            SuperlinkCallbackDictionary[kv.Key].Invoke(kv.Key);
+                        }
+                        else
+                        {
+                            UnityEngine.Debug.LogWarning(string.Format("没有找到事件 id : {0}", kv.Key));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/RichText.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 77e6b8885b7d82d45a22af8f91c1408e
+timeCreated: 1512116316
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 349 - 0
Assets/Tookits/TextUtility/RichTextImage.cs

@@ -0,0 +1,349 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	using textUtility;
+	using UnityEngine;
+	using UnityEngine.EventSystems;
+	using UnityEngine.UI;
+	
+	[Serializable]
+	public class RichTextImage : Image, IPointerClickHandler
+	{
+	    #region Config
+	
+	    private bool EnableOverflowClick;
+	    [SerializeField] private Button Button;
+	    [SerializeField] private RichText RichText;
+	
+	    private List<BaseMatch> SpriteMatches = new List<BaseMatch>();
+	    private List<BaseMatch> UnderlineMatches = new List<BaseMatch>();
+	    private List<BaseMatch> SuperlinkMatches = new List<BaseMatch>();
+
+	    public RichTextImageManager ImageManager;
+
+    private Dictionary<int, List<Vector3>> SuperlinkPositionsDictionary = new Dictionary<int, List<Vector3>>();
+	
+	    #endregion
+	
+	    protected override void OnPopulateMesh(VertexHelper toFill)
+	    {
+	        //Debug.Log(1);
+	
+	        toFill.Clear();
+	
+	        List<BaseMatch> baseMatches = new List<BaseMatch>();
+	        baseMatches.AddRange(SpriteMatches);
+	        baseMatches.AddRange(UnderlineMatches);
+	        baseMatches.AddRange(SuperlinkMatches);
+	        //Debug.Log(baseMatches.Count);
+	        List<int> triangles = new List<int>();
+	        List<UIVertex> vertices = new List<UIVertex>();
+	        foreach (var baseMatch in baseMatches)
+	        {
+	            List<List<UIVertex>> verticesList = GetRenderVerticesList(baseMatch);
+	            foreach (var tempVertices in verticesList)
+	            {
+	                triangles.AddRange(GetTriangles(vertices));
+	                vertices.AddRange(tempVertices);
+	            }
+	        }
+	        //Debug.Log(vertices.Count);
+	        //Debug.Log(triangles.Count);
+	        toFill.AddUIVertexStream(vertices, triangles);
+	    }
+	
+	
+	    public void AddMatch(BaseMatch baseMatch)
+	    {
+            if (baseMatch is SpriteMatch)
+	        {
+	            SpriteMatches.Add(baseMatch);
+	            sprite = Sprite.Create((baseMatch as SpriteMatch).SpriteInfo.Sprite.texture, new Rect(), new Vector2(0.5f, 0.5f));
+	        }
+	        else if (baseMatch is UnderlineMatch)
+	        {
+	            UnderlineMatches.Add(baseMatch);
+	        }
+	        else if (baseMatch is SuperlinkMatch)
+	        {
+	            SuperlinkMatches.Add(baseMatch);
+	            if (EnableOverflowClick)
+	            {
+	                if (Button == null)
+	                {
+	                    Button = gameObject.AddComponent<Button>();
+	                }
+	                Button.enabled = true;
+	            }
+	        }
+	        else
+	        {
+	            throw new Exception();
+	        }
+	    }
+	
+	    public void ClearMatches()
+	    {
+	        SpriteMatches = new List<BaseMatch>();
+	        UnderlineMatches = new List<BaseMatch>();
+	        SuperlinkMatches = new List<BaseMatch>();
+	        SuperlinkPositionsDictionary = new Dictionary<int, List<Vector3>>();
+	        sprite = null;
+	        if (Button != null)
+	        {
+	            Button.enabled = false;
+	        }
+	    }
+	
+	    private List<List<UIVertex>> GetRenderVerticesList(BaseMatch baseMatch)
+	    {
+	        if (baseMatch is SpriteMatch)
+	        {
+	            return GetSpriteRenderVerticesList(baseMatch);
+	        }
+	        else if (baseMatch is UnderlineMatch)
+	        {
+	            return GetUnderlineRenderVerticesList(baseMatch);
+	        }
+	        else if (baseMatch is SuperlinkMatch)
+	        {
+	            return GetSuperlinkRenderVerticesList(baseMatch);
+	        }
+	        else
+	        {
+	            throw new Exception();
+	        }
+	    }
+	
+	    private List<List<UIVertex>> GetSpriteRenderVerticesList(BaseMatch baseMatch)
+	    {
+	        List<List<UIVertex>> verticesList = new List<List<UIVertex>>();
+	        SpriteMatch spriteMatch = (SpriteMatch) baseMatch;
+	        Vector3 middleCenter = new Vector3();
+	        float x;
+	        float y;
+	        int startIndex = spriteMatch.StartIndex;
+	        int endIndex = spriteMatch.EndIndex;
+	        if (startIndex == 0 || RichText.text[startIndex - 1] == '\n' || !RichText.IsSameLine(RichText.cachedTextGenerator, startIndex - 1, startIndex))
+	        {
+	            x = (ImageManager.Vertices[2 + endIndex*6].position.x + ImageManager.Vertices[4 + startIndex*6].position.x)/2;
+	        }
+	        else
+	        {
+	            startIndex--;
+	            x = (ImageManager.Vertices[2 + endIndex*6].position.x + ImageManager.Vertices[2 + startIndex*6].position.x)/2;
+	        }
+	        int lineIndex = RichText.GetLineIndex(RichText.cachedTextGenerator, startIndex);
+	        y = RichText.GetLineCenterY(lineIndex, RichText.cachedTextGenerator.lineCount);
+	        middleCenter.x = x;
+	        middleCenter.y = y;
+	        UIVertex upperLeft = new UIVertex();
+	        UIVertex upperRight = new UIVertex();
+	        UIVertex lowerRight = new UIVertex();
+	        UIVertex lowerLeft = new UIVertex();
+	        upperLeft.position = middleCenter + new Vector3(-spriteMatch.ScaledWidth/2, spriteMatch.ScaledHeight/2, 0);
+	        upperRight.position = middleCenter + new Vector3(spriteMatch.ScaledWidth/2, spriteMatch.ScaledHeight/2, 0);
+	        lowerRight.position = middleCenter + new Vector3(spriteMatch.ScaledWidth/2, -spriteMatch.ScaledHeight/2, 0);
+	        lowerLeft.position = middleCenter + new Vector3(-spriteMatch.ScaledWidth/2, -spriteMatch.ScaledHeight/2, 0);
+	        upperLeft.uv0 = spriteMatch.SpriteInfo.UVs[0];
+	        upperRight.uv0 = spriteMatch.SpriteInfo.UVs[1];
+	        lowerRight.uv0 = spriteMatch.SpriteInfo.UVs[2];
+	        lowerLeft.uv0 = spriteMatch.SpriteInfo.UVs[3];
+	        upperLeft.color = spriteMatch.SpriteInfo.SpriteSetting.Color;
+	        upperRight.color = spriteMatch.SpriteInfo.SpriteSetting.Color;
+	        lowerRight.color = spriteMatch.SpriteInfo.SpriteSetting.Color;
+	        lowerLeft.color = spriteMatch.SpriteInfo.SpriteSetting.Color;
+	        verticesList.Add(new List<UIVertex> {upperLeft, upperRight, lowerRight, lowerLeft});
+	        for (int i = 0; i < verticesList.Count; i++)
+	        {
+	            List<UIVertex> tempVertices = verticesList[i];
+	            Vector3 position0 = transform.TransformPoint(tempVertices[0].position);
+	            Vector3 position1 = transform.TransformPoint(tempVertices[1].position);
+	            Vector3 position2 = transform.TransformPoint(tempVertices[2].position);
+	            Vector3 position3 = transform.TransformPoint(tempVertices[3].position);
+	
+	            Debug.DrawLine(position0, position1, Color.red, 999);
+	            Debug.DrawLine(position1, position2, Color.red, 999);
+	            Debug.DrawLine(position2, position3, Color.red, 999);
+	            Debug.DrawLine(position3, position0, Color.red, 999);
+	        }
+	        return verticesList;
+	    }
+	
+	    private List<List<UIVertex>> GetUnderlineRenderVerticesList(BaseMatch baseMatch)
+	    {
+	        List<List<UIVertex>> verticesList = new List<List<UIVertex>>();
+	        UnderlineMatch underlineMatch = (UnderlineMatch) baseMatch;
+	        RichText.GetSegments(RichText.text, RichText.cachedTextGenerator, underlineMatch, underlineMatch.UnderlineSetting.UnderlineExcludeChars);
+	        for (int j = 0; j < underlineMatch.Segments.Count; j++)
+	        {
+	            int startIndex = underlineMatch.Segments[j][0];
+	            int endIndex = underlineMatch.Segments[j][1];
+	            int lineIndex = RichText.GetLineIndex(RichText.cachedTextGenerator, startIndex);
+	            float penPositionY = RichText.GetPenPositionY(lineIndex, RichText.cachedTextGenerator.lineCount);
+	            float charHeight = RichText.cachedTextGenerator.lines[lineIndex].height;
+	            Vector3 position0 = ImageManager.Vertices[4 + startIndex*6].position;
+	            Vector3 position1 = ImageManager.Vertices[2 + endIndex*6].position;
+	            position0.y = penPositionY;
+	            position1.y = penPositionY;
+	            Vector3 position2 = position1;
+	            Vector3 position3 = position0;
+	            position2.y -= charHeight* underlineMatch.UnderlineSetting.Scale;
+	            position3.y -= charHeight* underlineMatch.UnderlineSetting.Scale;
+	            UIVertex upperLeft = new UIVertex();
+	            UIVertex upperRight = new UIVertex();
+	            UIVertex lowerRight = new UIVertex();
+	            UIVertex lowerLeft = new UIVertex();
+	            upperLeft.position = position0;
+	            upperRight.position = position1;
+	            lowerRight.position = position2;
+	            lowerLeft.position = position3;
+	            upperLeft.color = underlineMatch.UnderlineSetting.Color;
+	            upperRight.color = underlineMatch.UnderlineSetting.Color;
+	            lowerRight.color = underlineMatch.UnderlineSetting.Color;
+	            lowerLeft.color = underlineMatch.UnderlineSetting.Color;
+	            verticesList.Add(new List<UIVertex> {upperLeft, upperRight, lowerRight, lowerLeft});
+	            position0 = transform.TransformPoint(position0);
+	            position1 = transform.TransformPoint(position1);
+	            position2 = transform.TransformPoint(position2);
+	            position3 = transform.TransformPoint(position3);
+	            Debug.DrawLine(position0, position1, Color.blue, 999);
+	            Debug.DrawLine(position1, position2, Color.blue, 999);
+	            Debug.DrawLine(position2, position3, Color.blue, 999);
+	            Debug.DrawLine(position3, position0, Color.blue, 999);
+	        }
+	        return verticesList;
+	    }
+	
+	    private List<List<UIVertex>> GetSuperlinkRenderVerticesList(BaseMatch baseMatch)
+	    {
+	        List<List<UIVertex>> verticesList = new List<List<UIVertex>>();
+	        SuperlinkMatch superlinkMatch = (SuperlinkMatch)baseMatch;
+	        RichText.GetSegments(RichText.text, RichText.cachedTextGenerator, superlinkMatch, superlinkMatch.SuperlinkSetting.SuperlinkExcludeChars);
+	        for (int j = 0; j < superlinkMatch.Segments.Count; j++)
+	        {
+	            int startIndex = superlinkMatch.Segments[j][0];
+	            int endIndex = superlinkMatch.Segments[j][1];
+	            int lineIndex = RichText.GetLineIndex(RichText.cachedTextGenerator, startIndex);
+	            float lineCenterY = RichText.GetLineCenterY(lineIndex, RichText.cachedTextGenerator.lineCount);
+	            float lineHeight = RichText.cachedTextGenerator.lines[lineIndex].height;
+	            EnableOverflowClick = superlinkMatch.SuperlinkSetting.EnableOverflowClick;
+	            float lineMaxY = lineCenterY + (lineHeight/2)* superlinkMatch.SuperlinkSetting.Scale;
+	            float lineMinY = lineCenterY - (lineHeight/2)* superlinkMatch.SuperlinkSetting.Scale;
+	            List<Vector3> superlinkPositions = new List<Vector3>();
+	            Vector3 position0 = ImageManager.Vertices[4 + startIndex*6].position;
+	            Vector3 position1 = ImageManager.Vertices[2 + endIndex*6].position;
+	            Vector3 position2 = position1;
+	            Vector3 position3 = position0;
+	            position0.y = lineMinY;
+	            position1.y = lineMinY;
+	            position2.y = lineMaxY;
+	            position3.y = lineMaxY;
+	            superlinkPositions.Add(position0);
+	            superlinkPositions.Add(position1);
+	            superlinkPositions.Add(position2);
+	            superlinkPositions.Add(position3);
+	            SuperlinkPositionsDictionary.Add(superlinkMatch.ID, superlinkPositions);
+	            position0 = transform.TransformPoint(position0);
+	            position1 = transform.TransformPoint(position1);
+	            position2 = transform.TransformPoint(position2);
+	            position3 = transform.TransformPoint(position3);
+	            Debug.DrawLine(position0, position1, Color.green, 999);
+	            Debug.DrawLine(position1, position2, Color.green, 999);
+	            Debug.DrawLine(position2, position3, Color.green, 999);
+	            Debug.DrawLine(position3, position0, Color.green, 999);
+	        }
+	        return verticesList;
+	    }
+	
+	    private List<int> GetTriangles(List<UIVertex> vertices)
+	    {
+	        List<int> triangles = new List<int>();
+	        triangles.Add(vertices.Count);
+	        triangles.Add(vertices.Count + 1);
+	        triangles.Add(vertices.Count + 2);
+	        triangles.Add(vertices.Count + 2);
+	        triangles.Add(vertices.Count + 3);
+	        triangles.Add(vertices.Count);
+	        return triangles;
+	    }
+	
+	
+	    public static RichTextImage CreateImage(RichText richText)
+	    {
+	        GameObject go = new GameObject("RichTextImage");
+	        go.transform.SetParent(richText.transform);
+	        RichTextImage image = go.AddComponent<RichTextImage>();
+	        image.RichText = richText;
+	        image.rectTransform.localPosition = Vector3.zero;
+	        image.rectTransform.localScale = Vector3.one;
+	        image.rectTransform.anchorMin = Vector2.zero;
+	        image.rectTransform.anchorMax = Vector2.one;
+	        image.rectTransform.offsetMin = Vector2.zero;
+	        image.rectTransform.offsetMax = Vector2.zero;
+	        image.rectTransform.sizeDelta = Vector2.zero;
+	        return image;
+	    }
+	
+	
+	    private void Update()
+	    {
+	        if (!EnableOverflowClick)
+	        {
+	            return;
+	        }
+	
+	        if (Input.GetMouseButtonDown(0)) //Overflow的情况下检测超链接是否被点击
+	        {
+	            Graphic graphic = GetComponent<Graphic>();
+	            Graphic selectedGraphic = EventSystem.current.currentSelectedGameObject == null ? null : EventSystem.current.currentSelectedGameObject.GetComponent<Graphic>();
+	            if (selectedGraphic == null)
+	            {
+	                OnPointerClick(Input.mousePosition);
+	            }
+	            else if (graphic != selectedGraphic && graphic.depth > selectedGraphic.depth)
+	            {
+	                OnPointerClick(Input.mousePosition);
+	            }
+	        }
+	    }
+	
+	    public void OnPointerClick(PointerEventData eventData)
+	    {
+	        //Vector3 clickPosition = eventData.position;
+	        //Debug.Log(clickPosition);
+	        OnPointerClick(eventData.position);
+	    }
+	
+	    public void OnPointerClick(Vector3 worldPosition)
+	    {
+	        Vector3 clickPosition = rectTransform.InverseTransformPoint(worldPosition);
+	        foreach (var kv in SuperlinkPositionsDictionary)
+	        {
+	            float minX = kv.Value.MyMin(vector => vector.x).x;
+	            float maxX = kv.Value.MyMax(vector => vector.x).x;
+	            float minY = kv.Value.MyMin(vector => vector.y).y;
+	            float maxY = kv.Value.MyMax(vector => vector.y).y;
+	            //Debug.Log(clickPosition);
+	            //Debug.Log(minX + "  " + maxX + "  " + minY + "  " + maxY);
+	            if ((minX <= clickPosition.x && clickPosition.x <= maxX) && (minY <= clickPosition.y && clickPosition.y <= maxY))
+	            {
+	                if (RichText.SuperlinkCallbackDictionary.ContainsKey(kv.Key))
+	                {
+	                    RichText.SuperlinkCallbackDictionary[kv.Key].Invoke(kv.Key);
+	                }
+	                else
+	                {
+	                    throw new Exception(string.Format("没有找到事件 id : {0}", kv.Key));
+	                }
+	            }
+	        }
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/RichTextImage.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: f4ca4d07d504bc445b43e54c1e46cc29
+timeCreated: 1512881340
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 161 - 0
Assets/Tookits/TextUtility/RichTextImageManager.cs

@@ -0,0 +1,161 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	using System.Linq;
+	using textUtility;
+	using UnityEngine;
+	using Object = UnityEngine.Object;
+	
+	[Serializable]
+	public class RichTextImageManager
+	{
+	    #region Config
+	
+	    [SerializeField] private bool Inited;
+	    [SerializeField] private RichText RichText;
+	    [SerializeField] public List<RichTextImage> Images;
+
+    public List<UIVertex> Vertices;
+
+    #endregion
+
+    public void Init(RichText richText)
+	    {
+	        if (Inited)
+	        {
+	            return;
+	        }
+	        else
+	        {
+	            Inited = true;
+	        }
+	        RichText = richText;
+	        Images = new List<RichTextImage>();
+	    }
+	
+	
+	    public void ClearRenderTask()
+	    {
+	        for (int i = 0; i < Images.Count; i++)
+	        {
+	            if (Images[i] == null)
+	            {
+	                Images.RemoveAt(i--);
+	                continue;
+	            }
+	            Images[i].ClearMatches();
+	            Images[i].gameObject.SetActive(false);
+	        }
+	    }
+	
+	    public void RenderSprites(List<BaseMatch> baseMatches)
+	    {
+	        if (baseMatches.Count == 0)
+	        {
+	            return;
+	        }
+	
+	        Dictionary<Texture2D, List<BaseMatch>> groupDictionary = new Dictionary<Texture2D, List<BaseMatch>>();
+	        foreach (var baseMatch in baseMatches)
+	        {
+	            SpriteMatch spriteMatch = (SpriteMatch) baseMatch;
+	            if (groupDictionary.ContainsKey(spriteMatch.SpriteInfo.Sprite.texture))
+	            {
+	                groupDictionary[spriteMatch.SpriteInfo.Sprite.texture].Add(spriteMatch);
+	            }
+	            else
+	            {
+	                groupDictionary.Add(spriteMatch.SpriteInfo.Sprite.texture, new List<BaseMatch>());
+	                groupDictionary[spriteMatch.SpriteInfo.Sprite.texture].Add(spriteMatch);
+	            }
+	        }
+	
+	        //int createCount = groupDictionary.Count - Images.Count;
+	        //for (int i = 0; i < createCount; i++)
+	        //{
+	        //    Images.Add(RichTextImage.CreateImage(RichText));
+	        //}
+	
+	        List<List<BaseMatch>> groups = groupDictionary.Values.ToList();
+	        for (int i = 0; i < groups.Count; i++)
+	        {
+	            RichTextImage image = GetRichTextImage();
+	            //Images[i].gameObject.SetActive(true);
+	            foreach (var baseMatch in groups[i])
+	            {
+	                image.AddMatch(baseMatch);
+	            }
+	        }
+	    }
+	
+	    public void RenderUnderlines(List<BaseMatch> baseMatches)
+	    {
+	        if (baseMatches.Count == 0)
+	        {
+	            return;
+	        }
+	
+	        //if (Images.Count == 0)
+	        //{
+	        //    Images.Add(RichTextImage.CreateImage(RichText));
+	        //}
+	        //Images[0].gameObject.SetActive(true);
+	        RichTextImage image = GetRichTextImage();
+	        foreach (var baseMatch in baseMatches)
+	        {
+	            image.AddMatch(baseMatch);
+	        }
+	    }
+	
+	    public void RenderSuperlinks(List<BaseMatch> baseMatches)
+	    {
+	        if (baseMatches.Count == 0)
+	        {
+	            return;
+	        }
+	
+	        //if (Images.Count == 0)
+	        //{
+	        //    Images.Add(RichTextImage.CreateImage(RichText));
+	        //}
+	        //Images[0].gameObject.SetActive(true);
+	
+	        RichTextImage image = GetRichTextImage();
+	        foreach (var baseMatch in baseMatches)
+	        {
+	            image.AddMatch(baseMatch);
+	        }
+	    }
+	
+	    public void DestroyAllImage()
+	    {
+	        for (int i = 0; i < Images.Count; i++)
+	        {
+	            Object.DestroyImmediate(Images[i]);
+	        }
+	        Images = new List<RichTextImage>();
+	    }
+	
+	    private RichTextImage GetRichTextImage()
+	    {
+	        foreach (var image in Images)
+	        {
+	            if (image.gameObject.activeSelf)
+	            {
+	                continue;
+	            }
+	            image.gameObject.SetActive(true);
+	            return image;
+	        }
+	        RichTextImage richTextImage = RichTextImage.CreateImage(RichText);
+	        Images.Add(richTextImage);
+	        return richTextImage;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/RichTextImageManager.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fa2cd553dbdfc8b4aa586ba5b6c880e6
+timeCreated: 1512881472
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/Setting.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a47ffb0c2f4561148a0ca906f601895e
+folderAsset: yes
+timeCreated: 1512787283
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 45 - 0
Assets/Tookits/TextUtility/Setting/BaseSetting.cs

@@ -0,0 +1,45 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	using Object = UnityEngine.Object;
+	
+	[Serializable]
+	public class BaseSetting
+	{
+	    #region Config
+	
+	    public int ID;
+	    public float Scale;
+	    public Color Color;
+	
+	    #endregion
+	
+	    public BaseSetting()
+	    {
+	
+	    }
+	
+	    //public static SpriteSetting GetDefaultSpriteSetting()
+	    //{
+	
+	    //}
+	
+	    //public static SpriteSetting GetDefaultSuperlinkSetting()
+	    //{
+	
+	    //}
+	
+	    //public static SpriteSetting GetDefaultUnderlineSetting()
+	    //{
+	
+	    //}
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Setting/BaseSetting.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ec216574f8f47e041bc59ca590d914b8
+timeCreated: 1512724097
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 30 - 0
Assets/Tookits/TextUtility/Setting/SpriteSetting.cs

@@ -0,0 +1,30 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	[Serializable]
+	public class SpriteSetting : BaseSetting
+	{
+	    #region Config
+	
+	    public Vector2 Offset;
+	
+	    #endregion
+	
+	    public SpriteSetting()
+	    {
+	        ID = 0;
+	        Scale = 1;
+	        Color = Color.white;
+	        Offset = Vector2.zero;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Setting/SpriteSetting.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b65fa92d24f50a8408b09b9502017013
+timeCreated: 1512787250
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 51 - 0
Assets/Tookits/TextUtility/Setting/SuperlinkSetting.cs

@@ -0,0 +1,51 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	[Serializable]
+	public class SuperlinkSetting : BaseSetting
+	{
+	    #region Config
+	
+	    public List<int> ExcludeCharASCIIs = new List<int>();
+	    public bool EnableOverflowClick;
+	
+	    public List<char> SuperlinkExcludeChars
+	    {
+	        get
+	        {
+	            if (!superlinkExcludeChars.IsAvailable())
+	            {
+	                superlinkExcludeChars = new List<char>();
+	                foreach (var ascii in ExcludeCharASCIIs)
+	                {
+	                    superlinkExcludeChars.Add((char)ascii);
+	                }
+	            }
+	            return superlinkExcludeChars;
+	        }
+	        set { superlinkExcludeChars = value; }
+	    }
+	    private List<char> superlinkExcludeChars;
+	
+	    #endregion
+	
+	    public SuperlinkSetting()
+	    {
+	        ID = 0;
+	        Scale = 0.75f;
+	        Color = Color.green;
+	        //ExcludeCharASCIIs.Add(9);
+	        ExcludeCharASCIIs.Add(10);
+	        //ExcludeCharASCIIs.Add(32);
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Setting/SuperlinkSetting.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 3556e7bd9b6a89e47ae23816deca2f1a
+timeCreated: 1512787270
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 52 - 0
Assets/Tookits/TextUtility/Setting/UnderlineSetting.cs

@@ -0,0 +1,52 @@
+namespace textUtility
+{
+
+
+	using System;
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	[Serializable]
+	public class UnderlineSetting : BaseSetting
+	{
+	    #region Config
+	
+	    public Vector2 Offset;
+	    public UnderlineStyle Style;
+	    public List<int> ExcludeCharASCIIs = new List<int>();
+	
+	    public List<char> UnderlineExcludeChars
+	    {
+	        get
+	        {
+	            if (!underlineExcludeChars.IsAvailable())
+	            {
+	                underlineExcludeChars = new List<char>();
+	                foreach (var ascii in ExcludeCharASCIIs)
+	                {
+	                    underlineExcludeChars.Add((char)ascii);
+	                }
+	            }
+	            return underlineExcludeChars;
+	        }
+	    }
+	    private List<char> underlineExcludeChars;
+	
+	    #endregion
+	
+	    public UnderlineSetting()
+	    {
+	        ID = 0;
+	        Scale = 0.1f;
+	        Color = Color.blue;
+	        Offset = Vector2.zero;
+	        //ExcludeCharASCIIs.Add(9);
+	        ExcludeCharASCIIs.Add(10);
+	        //ExcludeCharASCIIs.Add(32);
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/Setting/UnderlineSetting.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e681125669b9b8d4b8c780bfbb85fe26
+timeCreated: 1512787261
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/Tookits/TextUtility/Setting/UnderlineStyle.cs

@@ -0,0 +1,9 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public enum UnderlineStyle
+{
+    Ellipse,
+    Rectangle,
+}

+ 13 - 0
Assets/Tookits/TextUtility/Setting/UnderlineStyle.cs.meta

@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 10c10654901230943a977c50e8d07e5e
+timeCreated: 1515228477
+licenseType: Pro
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 68 - 0
Assets/Tookits/TextUtility/SpriteInfo.cs

@@ -0,0 +1,68 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	
+	public class SpriteInfo
+	{
+	    #region Config
+	
+	    public Sprite Sprite;
+	    public SpriteSetting SpriteSetting;
+	
+	    public List<Vector2> UVs
+	    {
+	        get
+	        {
+	            if (uvs.Count == 0)
+	            {
+	                Vector2 upperLeft = new Vector2(Sprite.rect.xMin / Sprite.texture.width, Sprite.rect.yMax / Sprite.texture.height);
+	                Vector2 upperRight = new Vector2(Sprite.rect.xMax / Sprite.texture.width, Sprite.rect.yMax / Sprite.texture.height);
+	                Vector2 lowerRight = new Vector2(Sprite.rect.xMax / Sprite.texture.width, Sprite.rect.yMin / Sprite.texture.height);
+	                Vector2 lowerLeft = new Vector2(Sprite.rect.xMin / Sprite.texture.width, Sprite.rect.yMin / Sprite.texture.height);
+	                uvs.Add(upperLeft);
+	                uvs.Add(upperRight);
+	                uvs.Add(lowerRight);
+	                uvs.Add(lowerLeft);
+	            }
+	            return uvs;
+	        }
+	        set { uvs = value; }
+	    }
+	    public List<Vector2> uvs = new List<Vector2>();
+	
+	    #endregion
+	
+	    public SpriteInfo(Sprite sprite, SpriteSetting spriteSetting = null)
+	    {
+	        Sprite = sprite;
+	        if (spriteSetting == null)
+	        {
+	            SpriteSetting = new SpriteSetting();
+	        }
+	        else
+	        {
+	            SpriteSetting = spriteSetting;
+	
+	        }
+	    }
+	
+	    public float GetScaledWidth(float referenceHeight)
+	    {
+	        float scale = (referenceHeight / Sprite.rect.height) * SpriteSetting.Scale;
+	        return Sprite.rect.width * scale;
+	    }
+	
+	    public float GetScaledHeight(float referenceWidth)
+	    {
+	        float scale = (referenceWidth / Sprite.rect.width) * SpriteSetting.Scale;
+	        return Sprite.rect.height * scale;
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/SpriteInfo.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: a655fb8793b57fe4d89762bf43d7547b
+timeCreated: 1512874651
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 93 - 0
Assets/Tookits/TextUtility/SpriteInfoManager.cs

@@ -0,0 +1,93 @@
+namespace textUtility
+{
+
+
+	using System;
+		    using UnityEngine.Events;
+		    using System.Collections;
+			using System.Collections.Generic;
+			using textUtility;
+			using UnityEngine;
+	
+	public class SpriteInfoManager : MonoBehaviour
+	{
+	    #region Config
+	
+	    public UnityEvent BuildTestEnviroment;
+	
+	    public static Dictionary<string, SpriteInfo> SpriteInfoDictionaryForEditor
+	    {
+	        get
+	        {
+	            if (spriteInfoDictionaryForEditor.Count == 0)
+	            {
+	                Instance.BuildTestEnviroment.Invoke();
+	                if (spriteInfoDictionaryForEditor.Count == 0)
+	                {
+	                    throw new Exception("没有搭建起测试环境");
+	                }
+	            }
+	            return spriteInfoDictionaryForEditor;
+	        }
+	        set { spriteInfoDictionaryForEditor = value; }
+	    }
+	
+	    public static Dictionary<string, SpriteInfo> spriteInfoDictionaryForEditor = new Dictionary<string, SpriteInfo>();
+	
+	    private static SpriteInfoManager Instance
+	    {
+	        get
+	        {
+	            if (instance == null)
+	            {
+	#if UNITY_EDITOR
+	                instance = InstanceManager.SearchInstance<SpriteInfoManager>();
+	#endif
+	            }
+	            return instance;
+	        }
+	    }
+	
+	    private static SpriteInfoManager instance;
+	
+	    public static Dictionary<string, SpriteInfo> SpriteInfoDictionary = new Dictionary<string, SpriteInfo>();
+	
+	    #endregion
+	
+	    public static void AddSpriteInfo(Sprite sprite, string infoName = null, SpriteSetting spriteSetting = null)
+	    {
+	        if (infoName == null)
+	        {
+	            infoName = sprite.name;
+	        }
+	        if (Application.isPlaying)
+	        {
+	            SpriteInfoDictionary.Add(infoName, new SpriteInfo(sprite, spriteSetting));
+	        }
+	        else
+	        {
+	            spriteInfoDictionaryForEditor.Add(infoName, new SpriteInfo(sprite, spriteSetting));
+	        }
+	    }
+	
+	    public static void Clear()
+	    {
+	        SpriteInfoDictionary = new Dictionary<string, SpriteInfo>();
+	        spriteInfoDictionaryForEditor = new Dictionary<string, SpriteInfo>();
+	    }
+	
+	    public static SpriteInfo GetSpriteInfo(string infoName)
+	    {
+	        if (Application.isPlaying)
+	        {
+	            return SpriteInfoDictionary[infoName];
+	        }
+	        else
+	        {
+	            return SpriteInfoDictionaryForEditor[infoName];
+	        }
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/SpriteInfoManager.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: dd89fc23e54d6124a8ea5482df52247b
+timeCreated: 1512870644
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 70 - 0
Assets/Tookits/TextUtility/SpriteInfoManager.prefab

@@ -0,0 +1,70 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+  m_ObjectHideFlags: 1
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications: []
+    m_RemovedComponents: []
+  m_ParentPrefab: {fileID: 0}
+  m_RootGameObject: {fileID: 1594177319052792}
+  m_IsPrefabParent: 1
+--- !u!1 &1594177319052792
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  serializedVersion: 5
+  m_Component:
+  - component: {fileID: 4977813053446552}
+  - component: {fileID: 114464247953004586}
+  m_Layer: 0
+  m_Name: SpriteInfoManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4977813053446552
+Transform:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1594177319052792}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &114464247953004586
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1594177319052792}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: dd89fc23e54d6124a8ea5482df52247b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  BuildTestEnviroment:
+    m_PersistentCalls:
+      m_Calls:
+      - m_Target: {fileID: 114218982850889332, guid: 403706d78c7b76e43baf3ef2853e588e,
+          type: 2}
+        m_MethodName: BuildTestEnviroment
+        m_Mode: 1
+        m_Arguments:
+          m_ObjectArgument: {fileID: 0}
+          m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
+          m_IntArgument: 0
+          m_FloatArgument: 0
+          m_StringArgument: 
+          m_BoolArgument: 0
+        m_CallState: 1
+    m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral,
+      PublicKeyToken=null

+ 8 - 0
Assets/Tookits/TextUtility/SpriteInfoManager.prefab.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fa8d40d0adc9ed84283dcf6c10c9382d
+timeCreated: 1512873712
+licenseType: Pro
+NativeFormatImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 21 - 0
Assets/Tookits/TextUtility/UITest.cs

@@ -0,0 +1,21 @@
+namespace textUtility
+{
+
+
+	using System.Collections;
+	using System.Collections.Generic;
+	
+	using UnityEngine;
+	using UnityEngine.EventSystems;
+	
+	public class UITest : EventSystem
+	{
+	    protected override void Update()
+	    {
+	        base.Update();
+	        Debug.Log(0);
+	    }
+	}
+
+
+}

+ 12 - 0
Assets/Tookits/TextUtility/UITest.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 02a0dfa1d6163d7449e376362abb9d55
+timeCreated: 1512790458
+licenseType: Pro
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: