You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

502 lines
23 KiB

  1. using UnityEngine;
  2. using System.Collections;
  3. namespace TMPro.Examples
  4. {
  5. [ExecuteInEditMode]
  6. public class TMP_TextInfoDebugTool : MonoBehaviour
  7. {
  8. public bool ShowCharacters;
  9. public bool ShowWords;
  10. public bool ShowLinks;
  11. public bool ShowLines;
  12. public bool ShowMeshBounds;
  13. public bool ShowTextBounds;
  14. [Space(10)]
  15. [TextArea(2, 2)]
  16. public string ObjectStats;
  17. private TMP_Text m_TextComponent;
  18. private Transform m_Transform;
  19. // Since this script is used for visual debugging, we exclude most of it in builds.
  20. #if UNITY_EDITOR
  21. void OnEnable()
  22. {
  23. m_TextComponent = gameObject.GetComponent<TMP_Text>();
  24. if (m_Transform == null)
  25. m_Transform = gameObject.GetComponent<Transform>();
  26. }
  27. void OnDrawGizmos()
  28. {
  29. // Update Text Statistics
  30. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  31. ObjectStats = "Characters: " + textInfo.characterCount + " Words: " + textInfo.wordCount + " Spaces: " + textInfo.spaceCount + " Sprites: " + textInfo.spriteCount + " Links: " + textInfo.linkCount
  32. + "\nLines: " + textInfo.lineCount + " Pages: " + textInfo.pageCount;
  33. // Draw Quads around each of the Characters
  34. #region Draw Characters
  35. if (ShowCharacters)
  36. DrawCharactersBounds();
  37. #endregion
  38. // Draw Quads around each of the words
  39. #region Draw Words
  40. if (ShowWords)
  41. DrawWordBounds();
  42. #endregion
  43. // Draw Quads around each of the words
  44. #region Draw Links
  45. if (ShowLinks)
  46. DrawLinkBounds();
  47. #endregion
  48. // Draw Quads around each line
  49. #region Draw Lines
  50. if (ShowLines)
  51. DrawLineBounds();
  52. #endregion
  53. // Draw Quad around the bounds of the text
  54. #region Draw Bounds
  55. if (ShowMeshBounds)
  56. DrawBounds();
  57. #endregion
  58. // Draw Quad around the rendered region of the text.
  59. #region Draw Text Bounds
  60. if (ShowTextBounds)
  61. DrawTextBounds();
  62. #endregion
  63. }
  64. /// <summary>
  65. /// Method to draw a rectangle around each character.
  66. /// </summary>
  67. /// <param name="text"></param>
  68. void DrawCharactersBounds()
  69. {
  70. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  71. for (int i = 0; i < textInfo.characterCount; i++)
  72. {
  73. // Draw visible as well as invisible characters
  74. TMP_CharacterInfo cInfo = textInfo.characterInfo[i];
  75. bool isCharacterVisible = i >= m_TextComponent.maxVisibleCharacters ||
  76. cInfo.lineNumber >= m_TextComponent.maxVisibleLines ||
  77. (m_TextComponent.overflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true;
  78. if (!isCharacterVisible) continue;
  79. // Get Bottom Left and Top Right position of the current character
  80. Vector3 bottomLeft = m_Transform.TransformPoint(cInfo.bottomLeft);
  81. Vector3 topLeft = m_Transform.TransformPoint(new Vector3(cInfo.topLeft.x, cInfo.topLeft.y, 0));
  82. Vector3 topRight = m_Transform.TransformPoint(cInfo.topRight);
  83. Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(cInfo.bottomRight.x, cInfo.bottomRight.y, 0));
  84. Color color = cInfo.isVisible ? Color.yellow : Color.grey;
  85. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, color);
  86. // Baseline
  87. Vector3 baselineStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0);
  88. Vector3 baselineEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0);
  89. Gizmos.color = Color.cyan;
  90. Gizmos.DrawLine(baselineStart, baselineEnd);
  91. // Draw Ascender & Descender for each character.
  92. Vector3 ascenderStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0);
  93. Vector3 ascenderEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0);
  94. Vector3 descenderStart = new Vector3(bottomLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0);
  95. Vector3 descenderEnd = new Vector3(bottomRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0);
  96. Gizmos.color = Color.cyan;
  97. Gizmos.DrawLine(ascenderStart, ascenderEnd);
  98. Gizmos.DrawLine(descenderStart, descenderEnd);
  99. // Draw Cap Height
  100. float capHeight = cInfo.baseLine + cInfo.fontAsset.fontInfo.CapHeight * cInfo.scale;
  101. Vector3 capHeightStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0);
  102. Vector3 capHeightEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0);
  103. Gizmos.color = Color.cyan;
  104. Gizmos.DrawLine(capHeightStart, capHeightEnd);
  105. // Draw xAdvance for each character.
  106. float xAdvance = m_Transform.TransformPoint(cInfo.xAdvance, 0, 0).x;
  107. Vector3 topAdvance = new Vector3(xAdvance, topLeft.y, 0);
  108. Vector3 bottomAdvance = new Vector3(xAdvance, bottomLeft.y, 0);
  109. Gizmos.color = Color.green;
  110. Gizmos.DrawLine(topAdvance, bottomAdvance);
  111. }
  112. }
  113. /// <summary>
  114. /// Method to draw rectangles around each word of the text.
  115. /// </summary>
  116. /// <param name="text"></param>
  117. void DrawWordBounds()
  118. {
  119. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  120. for (int i = 0; i < textInfo.wordCount; i++)
  121. {
  122. TMP_WordInfo wInfo = textInfo.wordInfo[i];
  123. bool isBeginRegion = false;
  124. Vector3 bottomLeft = Vector3.zero;
  125. Vector3 topLeft = Vector3.zero;
  126. Vector3 bottomRight = Vector3.zero;
  127. Vector3 topRight = Vector3.zero;
  128. float maxAscender = -Mathf.Infinity;
  129. float minDescender = Mathf.Infinity;
  130. Color wordColor = Color.green;
  131. // Iterate through each character of the word
  132. for (int j = 0; j < wInfo.characterCount; j++)
  133. {
  134. int characterIndex = wInfo.firstCharacterIndex + j;
  135. TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex];
  136. int currentLine = currentCharInfo.lineNumber;
  137. bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
  138. currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines ||
  139. (m_TextComponent.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true;
  140. // Track Max Ascender and Min Descender
  141. maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
  142. minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
  143. if (isBeginRegion == false && isCharacterVisible)
  144. {
  145. isBeginRegion = true;
  146. bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
  147. topLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
  148. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  149. // If Word is one character
  150. if (wInfo.characterCount == 1)
  151. {
  152. isBeginRegion = false;
  153. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  154. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  155. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  156. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  157. // Draw Region
  158. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor);
  159. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  160. }
  161. }
  162. // Last Character of Word
  163. if (isBeginRegion && j == wInfo.characterCount - 1)
  164. {
  165. isBeginRegion = false;
  166. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  167. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  168. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  169. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  170. // Draw Region
  171. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor);
  172. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  173. }
  174. // If Word is split on more than one line.
  175. else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber)
  176. {
  177. isBeginRegion = false;
  178. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  179. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  180. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  181. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  182. // Draw Region
  183. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor);
  184. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  185. maxAscender = -Mathf.Infinity;
  186. minDescender = Mathf.Infinity;
  187. }
  188. }
  189. //Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo));
  190. }
  191. }
  192. /// <summary>
  193. /// Draw rectangle around each of the links contained in the text.
  194. /// </summary>
  195. /// <param name="text"></param>
  196. void DrawLinkBounds()
  197. {
  198. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  199. for (int i = 0; i < textInfo.linkCount; i++)
  200. {
  201. TMP_LinkInfo linkInfo = textInfo.linkInfo[i];
  202. bool isBeginRegion = false;
  203. Vector3 bottomLeft = Vector3.zero;
  204. Vector3 topLeft = Vector3.zero;
  205. Vector3 bottomRight = Vector3.zero;
  206. Vector3 topRight = Vector3.zero;
  207. float maxAscender = -Mathf.Infinity;
  208. float minDescender = Mathf.Infinity;
  209. Color32 linkColor = Color.cyan;
  210. // Iterate through each character of the link text
  211. for (int j = 0; j < linkInfo.linkTextLength; j++)
  212. {
  213. int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
  214. TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex];
  215. int currentLine = currentCharInfo.lineNumber;
  216. bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
  217. currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines ||
  218. (m_TextComponent.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true;
  219. // Track Max Ascender and Min Descender
  220. maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
  221. minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
  222. if (isBeginRegion == false && isCharacterVisible)
  223. {
  224. isBeginRegion = true;
  225. bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
  226. topLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
  227. //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
  228. // If Link is one character
  229. if (linkInfo.linkTextLength == 1)
  230. {
  231. isBeginRegion = false;
  232. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  233. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  234. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  235. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  236. // Draw Region
  237. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor);
  238. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  239. }
  240. }
  241. // Last Character of Link
  242. if (isBeginRegion && j == linkInfo.linkTextLength - 1)
  243. {
  244. isBeginRegion = false;
  245. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  246. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  247. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  248. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  249. // Draw Region
  250. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor);
  251. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  252. }
  253. // If Link is split on more than one line.
  254. else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber)
  255. {
  256. isBeginRegion = false;
  257. topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
  258. bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
  259. bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
  260. topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
  261. // Draw Region
  262. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor);
  263. maxAscender = -Mathf.Infinity;
  264. minDescender = Mathf.Infinity;
  265. //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
  266. }
  267. }
  268. //Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo));
  269. }
  270. }
  271. /// <summary>
  272. /// Draw Rectangles around each lines of the text.
  273. /// </summary>
  274. /// <param name="text"></param>
  275. void DrawLineBounds()
  276. {
  277. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  278. for (int i = 0; i < textInfo.lineCount; i++)
  279. {
  280. TMP_LineInfo lineInfo = textInfo.lineInfo[i];
  281. bool isLineVisible = (lineInfo.characterCount == 1 && textInfo.characterInfo[lineInfo.firstCharacterIndex].character == 10) ||
  282. i > m_TextComponent.maxVisibleLines ||
  283. (m_TextComponent.overflowMode == TextOverflowModes.Page && textInfo.characterInfo[lineInfo.firstCharacterIndex].pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true;
  284. if (!isLineVisible) continue;
  285. //if (!ShowLinesOnlyVisibleCharacters)
  286. //{
  287. // Get Bottom Left and Top Right position of each line
  288. float ascender = lineInfo.ascender;
  289. float descender = lineInfo.descender;
  290. float baseline = lineInfo.baseline;
  291. float maxAdvance = lineInfo.maxAdvance;
  292. Vector3 bottomLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, descender, 0));
  293. Vector3 topLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, ascender, 0));
  294. Vector3 topRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, ascender, 0));
  295. Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, descender, 0));
  296. DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, Color.green);
  297. Vector3 bottomOrigin = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin, descender, 0));
  298. Vector3 topOrigin = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin, ascender, 0));
  299. Vector3 bottomAdvance = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin + maxAdvance, descender, 0));
  300. Vector3 topAdvance = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin + maxAdvance, ascender, 0));
  301. DrawDottedRectangle(bottomOrigin, topOrigin, topAdvance, bottomAdvance, Color.green);
  302. Vector3 baselineStart = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, baseline, 0));
  303. Vector3 baselineEnd = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, baseline, 0));
  304. Gizmos.color = Color.cyan;
  305. Gizmos.DrawLine(baselineStart, baselineEnd);
  306. // Draw LineExtents
  307. Gizmos.color = Color.grey;
  308. Gizmos.DrawLine(m_Transform.TransformPoint(lineInfo.lineExtents.min), m_Transform.TransformPoint(lineInfo.lineExtents.max));
  309. //}
  310. //else
  311. //{
  312. //// Get Bottom Left and Top Right position of each line
  313. //float ascender = lineInfo.ascender;
  314. //float descender = lineInfo.descender;
  315. //Vector3 bottomLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, descender, 0));
  316. //Vector3 topLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, ascender, 0));
  317. //Vector3 topRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, ascender, 0));
  318. //Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, descender, 0));
  319. //DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, Color.green);
  320. //Vector3 baselineStart = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].baseLine, 0));
  321. //Vector3 baselineEnd = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].baseLine, 0));
  322. //Gizmos.color = Color.cyan;
  323. //Gizmos.DrawLine(baselineStart, baselineEnd);
  324. //}
  325. }
  326. }
  327. /// <summary>
  328. /// Draw Rectangle around the bounds of the text object.
  329. /// </summary>
  330. void DrawBounds()
  331. {
  332. Bounds meshBounds = m_TextComponent.bounds;
  333. // Get Bottom Left and Top Right position of each word
  334. Vector3 bottomLeft = m_TextComponent.transform.position + (meshBounds.center - meshBounds.extents);
  335. Vector3 topRight = m_TextComponent.transform.position + (meshBounds.center + meshBounds.extents);
  336. DrawRectangle(bottomLeft, topRight, new Color(1, 0.5f, 0));
  337. }
  338. void DrawTextBounds()
  339. {
  340. Bounds textBounds = m_TextComponent.textBounds;
  341. Vector3 bottomLeft = m_TextComponent.transform.position + (textBounds.center - textBounds.extents);
  342. Vector3 topRight = m_TextComponent.transform.position + (textBounds.center + textBounds.extents);
  343. DrawRectangle(bottomLeft, topRight, new Color(0f, 0.5f, 0.5f));
  344. }
  345. // Draw Rectangles
  346. void DrawRectangle(Vector3 BL, Vector3 TR, Color color)
  347. {
  348. Gizmos.color = color;
  349. Gizmos.DrawLine(new Vector3(BL.x, BL.y, 0), new Vector3(BL.x, TR.y, 0));
  350. Gizmos.DrawLine(new Vector3(BL.x, TR.y, 0), new Vector3(TR.x, TR.y, 0));
  351. Gizmos.DrawLine(new Vector3(TR.x, TR.y, 0), new Vector3(TR.x, BL.y, 0));
  352. Gizmos.DrawLine(new Vector3(TR.x, BL.y, 0), new Vector3(BL.x, BL.y, 0));
  353. }
  354. // Draw Rectangles
  355. void DrawRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color)
  356. {
  357. Gizmos.color = color;
  358. Gizmos.DrawLine(bl, tl);
  359. Gizmos.DrawLine(tl, tr);
  360. Gizmos.DrawLine(tr, br);
  361. Gizmos.DrawLine(br, bl);
  362. }
  363. // Draw Rectangles
  364. void DrawDottedRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color)
  365. {
  366. var cam = Camera.current;
  367. float dotSpacing = (cam.WorldToScreenPoint(br).x - cam.WorldToScreenPoint(bl).x) / 75f;
  368. UnityEditor.Handles.color = color;
  369. UnityEditor.Handles.DrawDottedLine(bl, tl, dotSpacing);
  370. UnityEditor.Handles.DrawDottedLine(tl, tr, dotSpacing);
  371. UnityEditor.Handles.DrawDottedLine(tr, br, dotSpacing);
  372. UnityEditor.Handles.DrawDottedLine(br, bl, dotSpacing);
  373. }
  374. #endif
  375. }
  376. }