A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3using UnityEngine;
4using UnityEngine.Rendering;
5using UnityEngine.UI;
6using System.Collections.Generic;
7using Unity.Profiling;
8using UnityEngine.TextCore;
9using UnityEngine.TextCore.LowLevel;
10using Object = UnityEngine.Object;
11
12
13#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
14#pragma warning disable 0618 // Disabled warning due to SetVertices being deprecated until new release with SetMesh() is available.
15
16
17namespace TMPro
18{
19 [DisallowMultipleComponent]
20 [RequireComponent(typeof(RectTransform))]
21 [RequireComponent(typeof(CanvasRenderer))]
22 [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)]
23 [ExecuteAlways]
24 #if UNITY_2023_2_OR_NEWER
25 [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")]
26 #else
27 [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")]
28 #endif
29 public class TextMeshProUGUI : TMP_Text, ILayoutElement
30 {
31 /// <summary>
32 /// Get the material that will be used for rendering.
33 /// </summary>
34 public override Material materialForRendering
35 {
36 get { return TMP_MaterialManager.GetMaterialForRendering(this, m_sharedMaterial); }
37 }
38
39
40 /// <summary>
41 /// Determines if the size of the text container will be adjusted to fit the text object when it is first created.
42 /// </summary>
43 public override bool autoSizeTextContainer
44 {
45 get { return m_autoSizeTextContainer; }
46
47 set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); SetLayoutDirty(); } }
48 }
49
50
51 /// <summary>
52 /// Reference to the Mesh used by the text object.
53 /// </summary>
54 public override Mesh mesh
55 {
56 get { return m_mesh; }
57 }
58
59
60 /// <summary>
61 /// Reference to the CanvasRenderer used by the text object.
62 /// </summary>
63 public new CanvasRenderer canvasRenderer
64 {
65 get
66 {
67 if (m_canvasRenderer == null) m_canvasRenderer = GetComponent<CanvasRenderer>();
68
69 return m_canvasRenderer;
70 }
71 }
72
73
74 /// <summary>
75 /// Anchor dampening prevents the anchor position from being adjusted unless the positional change exceeds about 40% of the width of the underline character. This essentially stabilizes the anchor position.
76 /// </summary>
77 //public bool anchorDampening
78 //{
79 // get { return m_anchorDampening; }
80 // set { if (m_anchorDampening != value) { havePropertiesChanged = true; m_anchorDampening = value; /* ScheduleUpdate(); */ } }
81 //}
82
83 #if !UNITY_2019_3_OR_NEWER
84 [SerializeField]
85 private bool m_Maskable = true;
86 #endif
87
88 private bool m_isRebuildingLayout = false;
89 private Coroutine m_DelayedGraphicRebuild;
90 private Coroutine m_DelayedMaterialRebuild;
91
92 /// <summary>
93 /// Function called by Unity when the horizontal layout needs to be recalculated.
94 /// </summary>
95 public void CalculateLayoutInputHorizontal()
96 {
97 //Debug.Log("*** CalculateLayoutHorizontal() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***");
98 }
99
100
101 /// <summary>
102 /// Function called by Unity when the vertical layout needs to be recalculated.
103 /// </summary>
104 public void CalculateLayoutInputVertical()
105 {
106 //Debug.Log("*** CalculateLayoutInputVertical() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***");
107 }
108
109
110 public override void SetVerticesDirty()
111 {
112 if (this == null || !this.IsActive())
113 return;
114
115 if (CanvasUpdateRegistry.IsRebuildingGraphics())
116 return;
117
118 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
119
120 if (m_OnDirtyVertsCallback != null)
121 m_OnDirtyVertsCallback();
122 }
123
124
125 /// <summary>
126 ///
127 /// </summary>
128 public override void SetLayoutDirty()
129 {
130 m_isPreferredWidthDirty = true;
131 m_isPreferredHeightDirty = true;
132
133 if (this == null || !this.IsActive())
134 return;
135
136 LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform);
137
138 m_isLayoutDirty = true;
139
140 if (m_OnDirtyLayoutCallback != null)
141 m_OnDirtyLayoutCallback();
142 }
143
144
145 /// <summary>
146 ///
147 /// </summary>
148 public override void SetMaterialDirty()
149 {
150 if (this == null || !this.IsActive())
151 return;
152
153 if (CanvasUpdateRegistry.IsRebuildingGraphics())
154 return;
155
156 m_isMaterialDirty = true;
157 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
158
159 if (m_OnDirtyMaterialCallback != null)
160 m_OnDirtyMaterialCallback();
161 }
162
163
164 /// <summary>
165 ///
166 /// </summary>
167 public override void SetAllDirty()
168 {
169 SetLayoutDirty();
170 SetVerticesDirty();
171 SetMaterialDirty();
172 }
173
174
175 /// <summary>
176 /// Delay registration of text object for graphic rebuild by one frame.
177 /// </summary>
178 /// <returns></returns>
179 IEnumerator DelayedGraphicRebuild()
180 {
181 yield return null;
182
183 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
184
185 if (m_OnDirtyVertsCallback != null)
186 m_OnDirtyVertsCallback();
187
188 m_DelayedGraphicRebuild = null;
189 }
190
191
192 /// <summary>
193 /// Delay registration of text object for graphic rebuild by one frame.
194 /// </summary>
195 /// <returns></returns>
196 IEnumerator DelayedMaterialRebuild()
197 {
198 yield return null;
199
200 m_isMaterialDirty = true;
201 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
202
203 if (m_OnDirtyMaterialCallback != null)
204 m_OnDirtyMaterialCallback();
205
206 m_DelayedMaterialRebuild = null;
207 }
208
209
210 /// <summary>
211 ///
212 /// </summary>
213 /// <param name="update"></param>
214 public override void Rebuild(CanvasUpdate update)
215 {
216 if (this == null) return;
217
218 if (update == CanvasUpdate.Prelayout)
219 {
220 if (m_autoSizeTextContainer)
221 {
222 m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity);
223 }
224 }
225 else if (update == CanvasUpdate.PreRender)
226 {
227 OnPreRenderCanvas();
228
229 if (!m_isMaterialDirty) return;
230
231 UpdateMaterial();
232 m_isMaterialDirty = false;
233 }
234 }
235
236
237 /// <summary>
238 /// Method to keep the pivot of the sub text objects in sync with the parent pivot.
239 /// </summary>
240 private void UpdateSubObjectPivot()
241 {
242 if (m_textInfo == null) return;
243
244 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
245 {
246 m_subTextObjects[i].SetPivotDirty();
247 }
248 //m_isPivotDirty = false;
249 }
250
251
252 /// <summary>
253 ///
254 /// </summary>
255 /// <param name="baseMaterial"></param>
256 /// <returns></returns>
257 public override Material GetModifiedMaterial(Material baseMaterial)
258 {
259 Material mat = baseMaterial;
260
261 if (m_ShouldRecalculateStencil)
262 {
263 var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
264 m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;
265 m_ShouldRecalculateStencil = false;
266 }
267
268 if (m_StencilValue > 0)
269 {
270 var maskMat = StencilMaterial.Add(mat, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
271 StencilMaterial.Remove(m_MaskMaterial);
272 m_MaskMaterial = maskMat;
273 mat = m_MaskMaterial;
274 }
275
276 return mat;
277 }
278
279
280 /// <summary>
281 ///
282 /// </summary>
283 protected override void UpdateMaterial()
284 {
285 //Debug.Log("*** UpdateMaterial() ***");
286
287 //if (!this.IsActive())
288 // return;
289
290 if (m_sharedMaterial == null || canvasRenderer == null) return;
291
292 m_canvasRenderer.materialCount = 1;
293 m_canvasRenderer.SetMaterial(materialForRendering, 0);
294 //m_canvasRenderer.SetTexture(materialForRendering.mainTexture);
295 }
296
297
298 //public override void OnRebuildRequested()
299 //{
300 // //Debug.Log("OnRebuildRequested");
301
302 // base.OnRebuildRequested();
303 //}
304
305
306
307 //public override bool Raycast(Vector2 sp, Camera eventCamera)
308 //{
309 // //Debug.Log("Raycast Event. ScreenPoint: " + sp);
310 // return base.Raycast(sp, eventCamera);
311 //}
312
313
314 // MASKING RELATED PROPERTIES
315 /// <summary>
316 /// Sets the masking offset from the bounds of the object
317 /// </summary>
318 public Vector4 maskOffset
319 {
320 get { return m_maskOffset; }
321 set { m_maskOffset = value; UpdateMask(); m_havePropertiesChanged = true; }
322 }
323
324
325 //public override Material defaultMaterial
326 //{
327 // get { Debug.Log("Default Material called."); return m_sharedMaterial; }
328 //}
329
330
331
332 //protected override void OnCanvasHierarchyChanged()
333 //{
334 // //Debug.Log("OnCanvasHierarchyChanged...");
335 //}
336
337
338 // IClippable implementation
339 /// <summary>
340 /// Method called when the state of a parent changes.
341 /// </summary>
342 public override void RecalculateClipping()
343 {
344 //Debug.Log("***** RecalculateClipping() *****");
345
346 base.RecalculateClipping();
347 }
348
349
350 // IMaskable Implementation
351 /// <summary>
352 /// Method called when Stencil Mask needs to be updated on this element and parents.
353 /// </summary>
354 // public override void RecalculateMasking()
355 // {
356 // //Debug.Log("***** RecalculateMasking() *****");
357 //
358 // this.m_ShouldRecalculateStencil = true;
359 // SetMaterialDirty();
360 // }
361
362
363 //public override void SetClipRect(Rect clipRect, bool validRect)
364 //{
365 // //Debug.Log("***** SetClipRect (" + clipRect + ", " + validRect + ") *****");
366
367 // base.SetClipRect(clipRect, validRect);
368 //}
369
370
371 /// <summary>
372 /// Override of the Cull function to provide for the ability to override the culling of the text object.
373 /// </summary>
374 /// <param name="clipRect"></param>
375 /// <param name="validRect"></param>
376 public override void Cull(Rect clipRect, bool validRect)
377 {
378 m_ShouldUpdateCulling = false;
379
380 // Delay culling check in the event the text layout is dirty and geometry has to be updated.
381 if (m_isLayoutDirty)
382 {
383 m_ShouldUpdateCulling = true;
384 m_ClipRect = clipRect;
385 m_ValidRect = validRect;
386 return;
387 }
388
389 // Get compound rect for the text object and sub text objects in local canvas space.
390 Rect rect = GetCanvasSpaceClippingRect();
391
392 // No point culling if geometry bounds have no width or height.
393 //if (rect.width == 0 || rect.height == 0)
394 // return;
395
396 var cull = !validRect || !clipRect.Overlaps(rect, true);
397 if (m_canvasRenderer.cull != cull)
398 {
399 m_canvasRenderer.cull = cull;
400 onCullStateChanged.Invoke(cull);
401 OnCullingChanged();
402
403 // Update any potential sub mesh objects
404 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
405 {
406 m_subTextObjects[i].canvasRenderer.cull = cull;
407 }
408 }
409 }
410
411 private bool m_ShouldUpdateCulling;
412 private Rect m_ClipRect;
413 private bool m_ValidRect;
414
415 /// <summary>
416 /// Internal function to allow delay of culling until the text geometry has been updated.
417 /// </summary>
418 internal override void UpdateCulling()
419 {
420 // Get compound rect for the text object and sub text objects in local canvas space.
421 Rect rect = GetCanvasSpaceClippingRect();
422
423 // No point culling if geometry bounds have no width or height.
424 //if (rect.width == 0 || rect.height == 0)
425 // return;
426
427 var cull = !m_ValidRect || !m_ClipRect.Overlaps(rect, true);
428 if (m_canvasRenderer.cull != cull)
429 {
430 m_canvasRenderer.cull = cull;
431 onCullStateChanged.Invoke(cull);
432 OnCullingChanged();
433
434 // Update any potential sub mesh objects
435 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
436 {
437 m_subTextObjects[i].canvasRenderer.cull = cull;
438 }
439 }
440
441 m_ShouldUpdateCulling = false;
442 }
443
444
445 /*
446 /// <summary>
447 /// Sets the mask type
448 /// </summary>
449 public MaskingTypes mask
450 {
451 get { return m_mask; }
452 set { m_mask = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
453 }
454
455
456 /// <summary>
457 /// Set the masking offset mode (as percentage or pixels)
458 /// </summary>
459 public MaskingOffsetMode maskOffsetMode
460 {
461 get { return m_maskOffsetMode; }
462 set { m_maskOffsetMode = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
463 }
464 */
465
466
467 /*
468 /// <summary>
469 /// Sets the softness of the mask
470 /// </summary>
471 public Vector2 maskSoftness
472 {
473 get { return m_maskSoftness; }
474 set { m_maskSoftness = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
475 }
476
477
478 /// <summary>
479 /// Allows to move / offset the mesh vertices by a set amount
480 /// </summary>
481 public Vector2 vertexOffset
482 {
483 get { return m_vertexOffset; }
484 set { m_vertexOffset = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
485 }
486 */
487
488
489 /// <summary>
490 /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script.
491 /// </summary>
492 public override void UpdateMeshPadding()
493 {
494 m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
495 m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
496 m_havePropertiesChanged = true;
497 checkPaddingRequired = false;
498
499 // Return if text object is not awake yet.
500 if (m_textInfo == null) return;
501
502 // Update sub text objects
503 for (int i = 1; i < m_textInfo.materialCount; i++)
504 m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold);
505 }
506
507
508 /// <summary>
509 /// Tweens the CanvasRenderer color associated with this Graphic.
510 /// </summary>
511 /// <param name="targetColor">Target color.</param>
512 /// <param name="duration">Tween duration.</param>
513 /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
514 /// <param name="useAlpha">Should also Tween the alpha channel?</param>
515 protected override void InternalCrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha)
516 {
517 if (m_textInfo == null)
518 return;
519
520 int materialCount = m_textInfo.materialCount;
521
522 for (int i = 1; i < materialCount; i++)
523 {
524 m_subTextObjects[i].CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha);
525 }
526 }
527
528
529 /// <summary>
530 /// Tweens the alpha of the CanvasRenderer color associated with this Graphic.
531 /// </summary>
532 /// <param name="alpha">Target alpha.</param>
533 /// <param name="duration">Duration of the tween in seconds.</param>
534 /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
535 protected override void InternalCrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale)
536 {
537 if (m_textInfo == null)
538 return;
539
540 int materialCount = m_textInfo.materialCount;
541
542 for (int i = 1; i < materialCount; i++)
543 {
544 m_subTextObjects[i].CrossFadeAlpha(alpha, duration, ignoreTimeScale);
545 }
546 }
547
548
549 /// <summary>
550 /// Function to force regeneration of the text object before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
551 /// </summary>
552 /// <param name="ignoreActiveState">Ignore Active State of text objects. Inactive objects are ignored by default.</param>
553 /// <param name="forceTextReparsing">Force re-parsing of the text.</param>
554 public override void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTextReparsing = false)
555 {
556 m_havePropertiesChanged = true;
557 m_ignoreActiveState = ignoreActiveState;
558
559 // Special handling in the event the Canvas is only disabled
560 if (m_canvas == null)
561 m_canvas = GetComponentInParent<Canvas>();
562
563 OnPreRenderCanvas();
564 }
565
566
567 /// <summary>
568 /// Function used to evaluate the length of a text string.
569 /// </summary>
570 /// <param name="text"></param>
571 /// <returns></returns>
572 public override TMP_TextInfo GetTextInfo(string text)
573 {
574 SetText(text);
575 SetArraySizes(m_TextProcessingArray);
576
577 m_renderMode = TextRenderFlags.DontRender;
578
579 ComputeMarginSize();
580
581 // Need to make sure we have a valid reference to a Canvas.
582 if (m_canvas == null) m_canvas = this.canvas;
583
584 GenerateTextMesh();
585
586 m_renderMode = TextRenderFlags.Render;
587
588 return this.textInfo;
589 }
590
591
592 /// <summary>
593 /// Function to clear the geometry of the Primary and Sub Text objects.
594 /// </summary>
595 public override void ClearMesh()
596 {
597 m_canvasRenderer.SetMesh(null);
598
599 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
600 m_subTextObjects[i].canvasRenderer.SetMesh(null);
601 }
602
603
604 /// <summary>
605 /// Event to allow users to modify the content of the text info before the text is rendered.
606 /// </summary>
607 public override event Action<TMP_TextInfo> OnPreRenderText;
608
609
610 /// <summary>
611 /// Function to update the geometry of the main and sub text objects.
612 /// </summary>
613 /// <param name="mesh"></param>
614 /// <param name="index"></param>
615 public override void UpdateGeometry(Mesh mesh, int index)
616 {
617 mesh.RecalculateBounds();
618
619 if (index == 0)
620 {
621 m_canvasRenderer.SetMesh(mesh);
622 }
623 else
624 {
625 m_subTextObjects[index].canvasRenderer.SetMesh(mesh);
626 }
627 }
628
629
630 /// <summary>
631 /// Function to upload the updated vertex data and renderer.
632 /// </summary>
633 public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags)
634 {
635 int materialCount = m_textInfo.materialCount;
636
637 for (int i = 0; i < materialCount; i++)
638 {
639 Mesh mesh;
640
641 if (i == 0)
642 mesh = m_mesh;
643 else
644 {
645 // Clear unused vertices
646 // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out.
647 //m_textInfo.meshInfo[i].ClearUnusedVertices();
648
649 mesh = m_subTextObjects[i].mesh;
650 }
651
652 if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices)
653 mesh.vertices = m_textInfo.meshInfo[i].vertices;
654
655 if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0)
656 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
657
658 if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2)
659 mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
660
661 //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4)
662 // mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
663
664 if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32)
665 mesh.colors32 = m_textInfo.meshInfo[i].colors32;
666
667 mesh.RecalculateBounds();
668
669 if (i == 0)
670 m_canvasRenderer.SetMesh(mesh);
671 else
672 m_subTextObjects[i].canvasRenderer.SetMesh(mesh);
673 }
674 }
675
676
677 /// <summary>
678 /// Function to upload the updated vertex data and renderer.
679 /// </summary>
680 public override void UpdateVertexData()
681 {
682 int materialCount = m_textInfo.materialCount;
683
684 for (int i = 0; i < materialCount; i++)
685 {
686 Mesh mesh;
687
688 if (i == 0)
689 mesh = m_mesh;
690 else
691 {
692 // Clear unused vertices
693 m_textInfo.meshInfo[i].ClearUnusedVertices();
694
695 mesh = m_subTextObjects[i].mesh;
696 }
697
698 //mesh.MarkDynamic();
699 mesh.vertices = m_textInfo.meshInfo[i].vertices;
700 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
701 mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
702 //mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
703 mesh.colors32 = m_textInfo.meshInfo[i].colors32;
704
705 mesh.RecalculateBounds();
706
707 if (i == 0)
708 m_canvasRenderer.SetMesh(mesh);
709 else
710 m_subTextObjects[i].canvasRenderer.SetMesh(mesh);
711 }
712 }
713
714
715 public void UpdateFontAsset()
716 {
717 LoadFontAsset();
718 }
719
720 #region TMPro_UGUI_Private
721 [SerializeField]
722 private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed.
723
724 protected TMP_SubMeshUI[] m_subTextObjects = new TMP_SubMeshUI[8];
725
726 private float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform;
727
728 private Vector3[] m_RectTransformCorners = new Vector3[4];
729 private CanvasRenderer m_canvasRenderer;
730 private Canvas m_canvas;
731 private float m_CanvasScaleFactor;
732
733
734 private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers.
735 private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer.
736 //private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text.
737
738 // MASKING RELATED PROPERTIES
739 // This property is now obsolete and used for compatibility with previous releases (prior to release 0.1.54).
740 [SerializeField]
741 private Material m_baseMaterial;
742
743 private bool m_isScrollRegionSet;
744 //private Mask m_mask;
745
746 [SerializeField]
747 private Vector4 m_maskOffset;
748
749 // Matrix used to animated Env Map
750 private Matrix4x4 m_EnvMapMatrix = new Matrix4x4();
751
752
753 //private bool m_isEnabled;
754 [NonSerialized]
755 private bool m_isRegisteredForEvents;
756
757 // Profiler Marker declarations
758 private static ProfilerMarker k_GenerateTextMarker = new ProfilerMarker("TMP.GenerateText");
759 private static ProfilerMarker k_SetArraySizesMarker = new ProfilerMarker("TMP.SetArraySizes");
760 private static ProfilerMarker k_GenerateTextPhaseIMarker = new ProfilerMarker("TMP GenerateText - Phase I");
761 private static ProfilerMarker k_ParseMarkupTextMarker = new ProfilerMarker("TMP Parse Markup Text");
762 private static ProfilerMarker k_CharacterLookupMarker = new ProfilerMarker("TMP Lookup Character & Glyph Data");
763 private static ProfilerMarker k_HandleGPOSFeaturesMarker = new ProfilerMarker("TMP Handle GPOS Features");
764 private static ProfilerMarker k_CalculateVerticesPositionMarker = new ProfilerMarker("TMP Calculate Vertices Position");
765 private static ProfilerMarker k_ComputeTextMetricsMarker = new ProfilerMarker("TMP Compute Text Metrics");
766 private static ProfilerMarker k_HandleVisibleCharacterMarker = new ProfilerMarker("TMP Handle Visible Character");
767 private static ProfilerMarker k_HandleWhiteSpacesMarker = new ProfilerMarker("TMP Handle White Space & Control Character");
768 private static ProfilerMarker k_HandleHorizontalLineBreakingMarker = new ProfilerMarker("TMP Handle Horizontal Line Breaking");
769 private static ProfilerMarker k_HandleVerticalLineBreakingMarker = new ProfilerMarker("TMP Handle Vertical Line Breaking");
770 private static ProfilerMarker k_SaveGlyphVertexDataMarker = new ProfilerMarker("TMP Save Glyph Vertex Data");
771 private static ProfilerMarker k_ComputeCharacterAdvanceMarker = new ProfilerMarker("TMP Compute Character Advance");
772 private static ProfilerMarker k_HandleCarriageReturnMarker = new ProfilerMarker("TMP Handle Carriage Return");
773 private static ProfilerMarker k_HandleLineTerminationMarker = new ProfilerMarker("TMP Handle Line Termination");
774 private static ProfilerMarker k_SavePageInfoMarker = new ProfilerMarker("TMP Save Page Info");
775 private static ProfilerMarker k_SaveTextExtentMarker = new ProfilerMarker("TMP Save Text Extent");
776 private static ProfilerMarker k_SaveProcessingStatesMarker = new ProfilerMarker("TMP Save Processing States");
777 private static ProfilerMarker k_GenerateTextPhaseIIMarker = new ProfilerMarker("TMP GenerateText - Phase II");
778 private static ProfilerMarker k_GenerateTextPhaseIIIMarker = new ProfilerMarker("TMP GenerateText - Phase III");
779
780
781 protected override void Awake()
782 {
783 //Debug.Log("***** Awake() called on object ID " + GetInstanceID() + ". *****");
784
785 #if UNITY_EDITOR
786 // Special handling for TMP Settings and importing Essential Resources
787 if (TMP_Settings.instance == null)
788 {
789 if (m_isWaitingOnResourceLoad == false)
790 TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
791
792 m_isWaitingOnResourceLoad = true;
793 return;
794 }
795 #endif
796
797 // Cache Reference to the Canvas
798 m_canvas = this.canvas;
799
800 m_isOrthographic = true;
801
802 // Cache Reference to RectTransform.
803 m_rectTransform = gameObject.GetComponent<RectTransform>();
804 if (m_rectTransform == null)
805 m_rectTransform = gameObject.AddComponent<RectTransform>();
806
807 // Cache a reference to the CanvasRenderer.
808 m_canvasRenderer = GetComponent<CanvasRenderer>();
809 if (m_canvasRenderer == null)
810 m_canvasRenderer = gameObject.AddComponent<CanvasRenderer> ();
811
812 if (m_mesh == null)
813 {
814 m_mesh = new Mesh();
815 m_mesh.hideFlags = HideFlags.HideAndDontSave;
816 #if DEVELOPMENT_BUILD || UNITY_EDITOR
817 m_mesh.name = "TextMeshPro UI Mesh";
818 #endif
819 // Create new TextInfo for the text object.
820 m_textInfo = new TMP_TextInfo(this);
821 }
822
823 // Load TMP Settings for new text object instances.
824 LoadDefaultSettings();
825
826 // Load the font asset and assign material to renderer.
827 LoadFontAsset();
828
829 // Allocate our initial buffers.
830 if (m_TextProcessingArray == null)
831 m_TextProcessingArray = new TextProcessingElement[m_max_characters];
832
833 m_cached_TextElement = new TMP_Character();
834 m_isFirstAllocation = true;
835
836 // Set flags to ensure our text is parsed and redrawn.
837 m_havePropertiesChanged = true;
838
839 m_isAwake = true;
840 }
841
842
843 protected override void OnEnable()
844 {
845 //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****");
846
847 // Return if Awake() has not been called on the text object.
848 if (m_isAwake == false)
849 return;
850
851 if (!m_isRegisteredForEvents)
852 {
853 //Debug.Log("Registering for Events.");
854
855 #if UNITY_EDITOR
856 // Register Callbacks for various events.
857 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
858 TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
859 TMPro_EventManager.TEXTMESHPRO_UGUI_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED);
860 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL);
861 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED);
862 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED);
863 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
864
865 UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
866 #endif
867 m_isRegisteredForEvents = true;
868 }
869
870 // Cache Reference to the Canvas
871 m_canvas = GetCanvas();
872
873 SetActiveSubMeshes(true);
874
875 // Register Graphic Component to receive event triggers
876 GraphicRegistry.RegisterGraphicForCanvas(m_canvas, this);
877
878 // Register text object for internal updates
879 if (m_IsTextObjectScaleStatic == false)
880 TMP_UpdateManager.RegisterTextObjectForUpdate(this);
881
882 ComputeMarginSize();
883
884 SetAllDirty();
885
886 RecalculateClipping();
887 RecalculateMasking();
888 }
889
890
891 protected override void OnDisable()
892 {
893 //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****");
894
895 // Return if Awake() has not been called on the text object.
896 if (m_isAwake == false)
897 return;
898
899 //if (m_MaskMaterial != null)
900 //{
901 // TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
902 // m_MaskMaterial = null;
903 //}
904
905 // UnRegister Graphic Component
906 GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this);
907 CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild((ICanvasElement)this);
908
909 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
910
911 if (m_canvasRenderer != null)
912 m_canvasRenderer.Clear();
913
914 SetActiveSubMeshes(false);
915
916 LayoutRebuilder.MarkLayoutForRebuild(m_rectTransform);
917 RecalculateClipping();
918 RecalculateMasking();
919 }
920
921
922 protected override void OnDestroy()
923 {
924 //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****");
925
926 // UnRegister Graphic Component
927 GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this);
928
929 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
930
931 // Clean up remaining mesh
932 if (m_mesh != null)
933 DestroyImmediate(m_mesh);
934
935 // Clean up mask material
936 if (m_MaskMaterial != null)
937 {
938 TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
939 m_MaskMaterial = null;
940 }
941
942 #if UNITY_EDITOR
943 // Unregister the event this object was listening to
944 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED);
945 TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED);
946 TMPro_EventManager.TEXTMESHPRO_UGUI_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED);
947 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL);
948 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED);
949 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED);
950 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
951 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
952
953 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;
954 #endif
955 m_isRegisteredForEvents = false;
956 }
957
958
959 #if UNITY_EDITOR
960 protected override void Reset()
961 {
962 //Debug.Log("***** Reset() *****"); //has been called.");
963
964 // Return if Awake() has not been called on the text object.
965 if (m_isAwake == false)
966 return;
967
968 LoadDefaultSettings();
969 LoadFontAsset();
970
971 m_havePropertiesChanged = true;
972 }
973
974
975 protected override void OnValidate()
976 {
977 //Debug.Log("***** OnValidate() ***** Frame:" + Time.frameCount); // ID " + GetInstanceID()); // New Material [" + m_sharedMaterial.name + "] with ID " + m_sharedMaterial.GetInstanceID() + ". Base Material is [" + m_baseMaterial.name + "] with ID " + m_baseMaterial.GetInstanceID() + ". Previous Base Material is [" + (m_lastBaseMaterial == null ? "Null" : m_lastBaseMaterial.name) + "].");
978
979 if (m_isAwake == false)
980 return;
981
982 // Handle Font Asset changes in the inspector.
983 if (m_fontAsset == null || m_hasFontAssetChanged)
984 {
985 LoadFontAsset();
986 m_hasFontAssetChanged = false;
987 }
988
989 if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null || m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset == null || m_fontAsset.atlasTexture.GetInstanceID() != m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
990 {
991 LoadFontAsset();
992 m_hasFontAssetChanged = false;
993 }
994
995 m_padding = GetPaddingForMaterial();
996 ComputeMarginSize();
997
998 m_inputSource = TextInputSources.TextInputBox;
999 m_havePropertiesChanged = true;
1000 m_isPreferredWidthDirty = true;
1001 m_isPreferredHeightDirty = true;
1002
1003 SetAllDirty();
1004 }
1005
1006
1007 /// <summary>
1008 /// Callback received when Prefabs are updated.
1009 /// </summary>
1010 /// <param name="go">The affected GameObject</param>
1011 void OnPrefabInstanceUpdate(GameObject go)
1012 {
1013 // Remove Callback if this prefab has been deleted.
1014 if (this == null)
1015 {
1016 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;
1017 return;
1018 }
1019
1020 if (go == this.gameObject)
1021 {
1022 TMP_SubMeshUI[] subTextObjects = GetComponentsInChildren<TMP_SubMeshUI>();
1023 if (subTextObjects.Length > 0)
1024 {
1025 for (int i = 0; i < subTextObjects.Length; i++)
1026 m_subTextObjects[i + 1] = subTextObjects[i];
1027 }
1028 }
1029 }
1030
1031
1032 // Event received when TMP resources have been loaded.
1033 void ON_RESOURCES_LOADED()
1034 {
1035 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
1036
1037 if (this == null)
1038 return;
1039
1040 m_isWaitingOnResourceLoad = false;
1041
1042 Awake();
1043 OnEnable();
1044 }
1045
1046
1047 // Event received when custom material editor properties are changed.
1048 void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
1049 {
1050 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received."); // Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " with ID:" + m_sharedMaterial.GetInstanceID() + " m_renderer.sharedMaterial: " + m_canvasRenderer.GetMaterial() + " Masking Material:" + m_MaskMaterial.GetInstanceID());
1051
1052 ShaderUtilities.GetShaderPropertyIDs(); // Initialize ShaderUtilities and get shader property IDs.
1053
1054 int materialID = mat.GetInstanceID();
1055 int sharedMaterialID = m_sharedMaterial.GetInstanceID();
1056 int maskingMaterialID = m_MaskMaterial == null ? 0 : m_MaskMaterial.GetInstanceID();
1057
1058 if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null)
1059 {
1060 if (m_canvasRenderer == null) return;
1061
1062 if (m_fontAsset != null)
1063 {
1064 m_canvasRenderer.SetMaterial(m_fontAsset.material, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
1065 //Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.");
1066 }
1067 else
1068 Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this);
1069 }
1070
1071
1072 if (m_canvasRenderer.GetMaterial() != m_sharedMaterial && m_fontAsset == null) // || m_renderer.sharedMaterials.Contains(mat))
1073 {
1074 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_uiRenderer.GetMaterial()); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name);
1075 m_sharedMaterial = m_canvasRenderer.GetMaterial();
1076 }
1077
1078
1079 // Make sure material properties are synchronized between the assigned material and masking material.
1080 if (m_MaskMaterial != null)
1081 {
1082 UnityEditor.Undo.RecordObject(m_MaskMaterial, "Material Property Changes");
1083 UnityEditor.Undo.RecordObject(m_sharedMaterial, "Material Property Changes");
1084
1085 if (materialID == sharedMaterialID)
1086 {
1087 //Debug.Log("Copy base material properties to masking material if not null.");
1088 float stencilID = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilID);
1089 float stencilComp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilComp);
1090 //float stencilOp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilOp);
1091 //float stencilRead = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilReadMask);
1092 //float stencilWrite = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilWriteMask);
1093
1094 m_MaskMaterial.CopyPropertiesFromMaterial(mat);
1095 m_MaskMaterial.shaderKeywords = mat.shaderKeywords;
1096
1097 m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
1098 m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp);
1099 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilOp, stencilOp);
1100 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, stencilID);
1101 //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 0);
1102 }
1103 else if (materialID == maskingMaterialID)
1104 {
1105 // Update the padding
1106 GetPaddingForMaterial(mat);
1107
1108 m_sharedMaterial.CopyPropertiesFromMaterial(mat);
1109 m_sharedMaterial.shaderKeywords = mat.shaderKeywords;
1110 m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilID, 0);
1111 m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilComp, 8);
1112 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilOp, 0);
1113 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, 255);
1114 //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 255);
1115 }
1116
1117 }
1118
1119 m_padding = GetPaddingForMaterial();
1120 ValidateEnvMapProperty();
1121 m_havePropertiesChanged = true;
1122 SetVerticesDirty();
1123 //SetMaterialDirty();
1124 }
1125
1126
1127 // Event received when font asset properties are changed in Font Inspector
1128 void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object font)
1129 {
1130 //if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset) font))
1131 {
1132 //Debug.Log("ON_FONT_PROPERTY_CHANGED event received.");
1133 m_havePropertiesChanged = true;
1134
1135 UpdateMeshPadding();
1136
1137 SetLayoutDirty();
1138 SetVerticesDirty();
1139 }
1140 }
1141
1142
1143 // Event received when UNDO / REDO Event alters the properties of the object.
1144 void ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(bool isChanged, Object obj)
1145 {
1146 //Debug.Log("Event Received by " + obj);
1147
1148 if (obj == this)
1149 {
1150 //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID());
1151 m_havePropertiesChanged = true;
1152
1153 ComputeMarginSize(); // Review this change
1154 SetVerticesDirty();
1155 }
1156 }
1157
1158
1159 // Event to Track Material Changed resulting from Drag-n-drop.
1160 void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
1161 {
1162 //Debug.Log("Drag-n-Drop Event - Receiving Object ID " + GetInstanceID() + ". Sender ID " + obj.GetInstanceID()); // + ". Prefab Parent is " + UnityEditor.PrefabUtility.GetPrefabParent(gameObject).GetInstanceID()); // + ". New Material is " + newMaterial.name + " with ID " + newMaterial.GetInstanceID() + ". Base Material is " + m_baseMaterial.name + " with ID " + m_baseMaterial.GetInstanceID());
1163
1164 // Check if event applies to this current object
1165 if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj)
1166 {
1167 UnityEditor.Undo.RecordObject(this, "Material Assignment");
1168 UnityEditor.Undo.RecordObject(m_canvasRenderer, "Material Assignment");
1169
1170 m_sharedMaterial = newMaterial;
1171
1172 m_padding = GetPaddingForMaterial();
1173
1174 m_havePropertiesChanged = true;
1175 SetVerticesDirty();
1176 SetMaterialDirty();
1177 }
1178 }
1179
1180
1181 // Event received when Text Styles are changed.
1182 void ON_TEXT_STYLE_CHANGED(bool isChanged)
1183 {
1184 m_havePropertiesChanged = true;
1185 SetVerticesDirty();
1186 }
1187
1188
1189 /// <summary>
1190 /// Event received when a Color Gradient Preset is modified.
1191 /// </summary>
1192 /// <param name="textObject"></param>
1193 void ON_COLOR_GRADIENT_CHANGED(Object gradient)
1194 {
1195 m_havePropertiesChanged = true;
1196 SetVerticesDirty();
1197 }
1198
1199
1200 /// <summary>
1201 /// Event received when the TMP Settings are changed.
1202 /// </summary>
1203 void ON_TMP_SETTINGS_CHANGED()
1204 {
1205 m_defaultSpriteAsset = null;
1206 m_havePropertiesChanged = true;
1207 SetAllDirty();
1208 }
1209 #endif
1210
1211
1212 // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer.
1213 protected override void LoadFontAsset()
1214 {
1215 //Debug.Log("***** LoadFontAsset() *****"); //TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") );
1216
1217 ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs.
1218
1219 if (m_fontAsset == null)
1220 {
1221 if (TMP_Settings.defaultFontAsset != null)
1222 m_fontAsset = TMP_Settings.defaultFontAsset;
1223
1224 if (m_fontAsset == null)
1225 {
1226 Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this);
1227 return;
1228 }
1229
1230 if (m_fontAsset.characterLookupTable == null)
1231 {
1232 Debug.Log("Dictionary is Null!");
1233 }
1234
1235 m_sharedMaterial = m_fontAsset.material;
1236 }
1237 else
1238 {
1239 // Read font definition if needed.
1240 if (m_fontAsset.characterLookupTable == null)
1241 m_fontAsset.ReadFontAssetDefinition();
1242
1243 // Added for compatibility with previous releases.
1244 if (m_sharedMaterial == null && m_baseMaterial != null)
1245 {
1246 m_sharedMaterial = m_baseMaterial;
1247 m_baseMaterial = null;
1248 }
1249
1250 // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset.
1251 if (m_sharedMaterial == null || m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
1252 {
1253 if (m_fontAsset.material == null)
1254 Debug.LogWarning("The Font Atlas Texture of the Font Asset " + m_fontAsset.name + " assigned to " + gameObject.name + " is missing.", this);
1255 else
1256 m_sharedMaterial = m_fontAsset.material;
1257 }
1258 }
1259
1260 // Cache environment map property validation.
1261 ValidateEnvMapProperty();
1262
1263 // Find and cache Underline & Ellipsis characters.
1264 GetSpecialCharacters(m_fontAsset);
1265
1266 m_padding = GetPaddingForMaterial();
1267
1268 SetMaterialDirty();
1269 }
1270
1271
1272 /// <summary>
1273 /// Method to retrieve the parent Canvas.
1274 /// </summary>
1275 private Canvas GetCanvas()
1276 {
1277 Canvas canvas = null;
1278 var list = TMP_ListPool<Canvas>.Get();
1279
1280 gameObject.GetComponentsInParent(false, list);
1281 if (list.Count > 0)
1282 {
1283 // Find the first active and enabled canvas.
1284 for (int i = 0; i < list.Count; ++i)
1285 {
1286 if (list[i].isActiveAndEnabled)
1287 {
1288 canvas = list[i];
1289 break;
1290 }
1291 }
1292 }
1293
1294 TMP_ListPool<Canvas>.Release(list);
1295
1296 return canvas;
1297 }
1298
1299 /// <summary>
1300 /// Method to check if the environment map property is valid.
1301 /// </summary>
1302 void ValidateEnvMapProperty()
1303 {
1304 if (m_sharedMaterial != null)
1305 m_hasEnvMapProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) && m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) != null;
1306 else
1307 m_hasEnvMapProperty = false;
1308 }
1309
1310 /// <summary>
1311 /// Method used when animating the Env Map on the material.
1312 /// </summary>
1313 void UpdateEnvMapMatrix()
1314 {
1315 if (!m_hasEnvMapProperty)
1316 return;
1317
1318 //Debug.Log("Updating Env Matrix...");
1319 Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation);
1320 #if !UNITY_EDITOR
1321 // The matrix property is reverted on editor save because m_sharedMaterial will be replaced with a new material instance.
1322 // Disable rotation change check if editor to handle this material change.
1323 if (m_currentEnvMapRotation == rotation)
1324 return;
1325 #endif
1326
1327 m_currentEnvMapRotation = rotation;
1328 m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(m_currentEnvMapRotation), Vector3.one);
1329
1330 m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix);
1331 }
1332
1333
1334 // Enable Masking in the Shader
1335 void EnableMasking()
1336 {
1337 if (m_fontMaterial == null)
1338 {
1339 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
1340 m_canvasRenderer.SetMaterial(m_fontMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
1341 }
1342
1343 m_sharedMaterial = m_fontMaterial;
1344 if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
1345 {
1346 m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1347 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1348 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1349
1350 UpdateMask(); // Update Masking Coordinates
1351 }
1352
1353 m_isMaskingEnabled = true;
1354
1355 //m_uiRenderer.SetMaterial(m_sharedMaterial, null);
1356
1357 //m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
1358 //m_alignmentPadding = ShaderUtilities.GetFontExtent(m_sharedMaterial);
1359
1360 /*
1361 Material mat = m_uiRenderer.GetMaterial();
1362 if (mat.HasProperty(ShaderUtilities.ID_MaskCoord))
1363 {
1364 mat.EnableKeyword("MASK_SOFT");
1365 mat.DisableKeyword("MASK_HARD");
1366 mat.DisableKeyword("MASK_OFF");
1367
1368 m_isMaskingEnabled = true;
1369 UpdateMask();
1370 }
1371 */
1372 }
1373
1374
1375 // Enable Masking in the Shader
1376 void DisableMasking()
1377 {
1378 /*
1379 if (m_fontMaterial != null)
1380 {
1381 if (m_stencilID > 0)
1382 m_sharedMaterial = m_MaskMaterial;
1383 else
1384 m_sharedMaterial = m_baseMaterial;
1385
1386 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
1387
1388 DestroyImmediate(m_fontMaterial);
1389 }
1390
1391 m_isMaskingEnabled = false;
1392 */
1393
1394 /*
1395 if (m_maskingMaterial != null && m_stencilID == 0)
1396 {
1397 m_sharedMaterial = m_baseMaterial;
1398 m_uiRenderer.SetMaterial(m_sharedMaterial, null);
1399 }
1400 else if (m_stencilID > 0)
1401 {
1402 m_sharedMaterial.EnableKeyword("MASK_OFF");
1403 m_sharedMaterial.DisableKeyword("MASK_HARD");
1404 m_sharedMaterial.DisableKeyword("MASK_SOFT");
1405 }
1406 */
1407
1408
1409 /*
1410 Material mat = m_uiRenderer.GetMaterial();
1411 if (mat.HasProperty(ShaderUtilities.ID_MaskCoord))
1412 {
1413 mat.EnableKeyword("MASK_OFF");
1414 mat.DisableKeyword("MASK_HARD");
1415 mat.DisableKeyword("MASK_SOFT");
1416
1417 m_isMaskingEnabled = false;
1418 UpdateMask();
1419 }
1420 */
1421 }
1422
1423
1424 // Update & recompute Mask offset
1425 void UpdateMask()
1426 {
1427 //Debug.Log("Updating Mask...");
1428
1429 if (m_rectTransform != null)
1430 {
1431 //Material mat = m_uiRenderer.GetMaterial();
1432 //if (mat == null || (m_overflowMode == TextOverflowModes.ScrollRect && m_isScrollRegionSet))
1433 // return;
1434
1435 if (!ShaderUtilities.isInitialized)
1436 ShaderUtilities.GetShaderPropertyIDs();
1437
1438 //Debug.Log("Setting Mask for the first time.");
1439
1440 m_isScrollRegionSet = true;
1441
1442 float softnessX = Mathf.Min(Mathf.Min(m_margin.x, m_margin.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX));
1443 float softnessY = Mathf.Min(Mathf.Min(m_margin.y, m_margin.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY));
1444
1445 softnessX = softnessX > 0 ? softnessX : 0;
1446 softnessY = softnessY > 0 ? softnessY : 0;
1447
1448 float width = (m_rectTransform.rect.width - Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2 + softnessX;
1449 float height = (m_rectTransform.rect.height - Mathf.Max(m_margin.y, 0) - Mathf.Max(m_margin.w, 0)) / 2 + softnessY;
1450
1451
1452 Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-Mathf.Max(m_margin.y, 0) + Mathf.Max(m_margin.w, 0)) / 2);
1453
1454 //Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (margin.x - margin.z) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-margin.y + margin.w) / 2);
1455 Vector4 mask = new Vector4(center.x, center.y, width, height);
1456 //Debug.Log(mask);
1457
1458
1459
1460 //Rect rect = new Rect(0, 0, m_rectTransform.rect.width + margin.x + margin.z, m_rectTransform.rect.height + margin.y + margin.w);
1461 //int softness = (int)m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX) / 2;
1462 m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask);
1463 }
1464 }
1465
1466
1467 // Function called internally when a new material is assigned via the fontMaterial property.
1468 protected override Material GetMaterial(Material mat)
1469 {
1470 // Get Shader PropertyIDs if they haven't been cached already.
1471 ShaderUtilities.GetShaderPropertyIDs();
1472
1473 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
1474 // This can occur when the Duplicate Material Context menu is used on an inactive object.
1475 //if (m_canvasRenderer == null)
1476 // m_canvasRenderer = GetComponent<CanvasRenderer>();
1477
1478 // Create Instance Material only if the new material is not the same instance previously used.
1479 if (m_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID())
1480 m_fontMaterial = CreateMaterialInstance(mat);
1481
1482 m_sharedMaterial = m_fontMaterial;
1483
1484 m_padding = GetPaddingForMaterial();
1485
1486 m_ShouldRecalculateStencil = true;
1487 SetVerticesDirty();
1488 SetMaterialDirty();
1489
1490 return m_sharedMaterial;
1491 }
1492
1493
1494 /// <summary>
1495 /// Method returning instances of the materials used by the text object.
1496 /// </summary>
1497 /// <returns></returns>
1498 protected override Material[] GetMaterials(Material[] mats)
1499 {
1500 int materialCount = m_textInfo.materialCount;
1501
1502 if (m_fontMaterials == null)
1503 m_fontMaterials = new Material[materialCount];
1504 else if (m_fontMaterials.Length != materialCount)
1505 TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false);
1506
1507 // Get instances of the materials
1508 for (int i = 0; i < materialCount; i++)
1509 {
1510 if (i == 0)
1511 m_fontMaterials[i] = fontMaterial;
1512 else
1513 m_fontMaterials[i] = m_subTextObjects[i].material;
1514 }
1515
1516 m_fontSharedMaterials = m_fontMaterials;
1517
1518 return m_fontMaterials;
1519 }
1520
1521
1522 // Function called internally when a new shared material is assigned via the fontSharedMaterial property.
1523 protected override void SetSharedMaterial(Material mat)
1524 {
1525 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
1526 // This can occur when the Duplicate Material Context menu is used on an inactive object.
1527 //if (m_canvasRenderer == null)
1528 // m_canvasRenderer = GetComponent<CanvasRenderer>();
1529
1530 m_sharedMaterial = mat;
1531
1532 m_padding = GetPaddingForMaterial();
1533
1534 SetMaterialDirty();
1535 }
1536
1537
1538 /// <summary>
1539 /// Method returning an array containing the materials used by the text object.
1540 /// </summary>
1541 /// <returns></returns>
1542 protected override Material[] GetSharedMaterials()
1543 {
1544 int materialCount = m_textInfo.materialCount;
1545
1546 if (m_fontSharedMaterials == null)
1547 m_fontSharedMaterials = new Material[materialCount];
1548 else if (m_fontSharedMaterials.Length != materialCount)
1549 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
1550
1551 for (int i = 0; i < materialCount; i++)
1552 {
1553 if (i == 0)
1554 m_fontSharedMaterials[i] = m_sharedMaterial;
1555 else
1556 m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial;
1557 }
1558
1559 return m_fontSharedMaterials;
1560 }
1561
1562
1563 /// <summary>
1564 /// Method used to assign new materials to the text and sub text objects.
1565 /// </summary>
1566 protected override void SetSharedMaterials(Material[] materials)
1567 {
1568 int materialCount = m_textInfo.materialCount;
1569
1570 // Check allocation of the fontSharedMaterials array.
1571 if (m_fontSharedMaterials == null)
1572 m_fontSharedMaterials = new Material[materialCount];
1573 else if (m_fontSharedMaterials.Length != materialCount)
1574 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
1575
1576 // Only assign as many materials as the text object contains.
1577 for (int i = 0; i < materialCount; i++)
1578 {
1579 if (i == 0)
1580 {
1581 // Only assign new material if the font atlas textures match.
1582 if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
1583 continue;
1584
1585 m_sharedMaterial = m_fontSharedMaterials[i] = materials[i];
1586 m_padding = GetPaddingForMaterial(m_sharedMaterial);
1587 }
1588 else
1589 {
1590 // Only assign new material if the font atlas textures match.
1591 if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
1592 continue;
1593
1594 // Only assign a new material if none were specified in the text input.
1595 if (m_subTextObjects[i].isDefaultMaterial)
1596 m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i];
1597 }
1598 }
1599 }
1600
1601
1602 // This function will create an instance of the Font Material.
1603 protected override void SetOutlineThickness(float thickness)
1604 {
1605 // Use material instance if one exists. Otherwise, create a new instance of the shared material.
1606 if (m_fontMaterial != null && m_sharedMaterial.GetInstanceID() != m_fontMaterial.GetInstanceID())
1607 {
1608 m_sharedMaterial = m_fontMaterial;
1609 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
1610 }
1611 else if(m_fontMaterial == null)
1612 {
1613 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
1614 m_sharedMaterial = m_fontMaterial;
1615 m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
1616 }
1617
1618 thickness = Mathf.Clamp01(thickness);
1619 m_sharedMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness);
1620 m_padding = GetPaddingForMaterial();
1621 }
1622
1623
1624 // This function will create an instance of the Font Material.
1625 protected override void SetFaceColor(Color32 color)
1626 {
1627 // Use material instance if one exists. Otherwise, create a new instance of the shared material.
1628 if (m_fontMaterial == null)
1629 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
1630
1631 m_sharedMaterial = m_fontMaterial;
1632 m_padding = GetPaddingForMaterial();
1633
1634 m_sharedMaterial.SetColor(ShaderUtilities.ID_FaceColor, color);
1635 }
1636
1637
1638 // This function will create an instance of the Font Material.
1639 protected override void SetOutlineColor(Color32 color)
1640 {
1641 // Use material instance if one exists. Otherwise, create a new instance of the shared material.
1642 if (m_fontMaterial == null)
1643 m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
1644
1645 m_sharedMaterial = m_fontMaterial;
1646 m_padding = GetPaddingForMaterial();
1647
1648 m_sharedMaterial.SetColor(ShaderUtilities.ID_OutlineColor, color);
1649 }
1650
1651
1652 // Sets the Render Queue and Ztest mode
1653 protected override void SetShaderDepth()
1654 {
1655 if (m_canvas == null || m_sharedMaterial == null)
1656 return;
1657
1658 if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay || m_isOverlay)
1659 {
1660 // Should this use an instanced material?
1661 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0);
1662 }
1663 else
1664 { // TODO: This section needs to be tested.
1665 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
1666 }
1667 }
1668
1669
1670 // Sets the Culling mode of the material
1671 protected override void SetCulling()
1672 {
1673 if (m_isCullingEnabled)
1674 {
1675 Material mat = materialForRendering;
1676
1677 if (mat != null)
1678 mat.SetFloat("_CullMode", 2);
1679
1680 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
1681 {
1682 mat = m_subTextObjects[i].materialForRendering;
1683
1684 if (mat != null)
1685 {
1686 mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2);
1687 }
1688 }
1689 }
1690 else
1691 {
1692 Material mat = materialForRendering;
1693
1694 if (mat != null)
1695 mat.SetFloat("_CullMode", 0);
1696
1697 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
1698 {
1699 mat = m_subTextObjects[i].materialForRendering;
1700
1701 if (mat != null)
1702 {
1703 mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0);
1704 }
1705 }
1706 }
1707 }
1708
1709
1710 // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective
1711 void SetPerspectiveCorrection()
1712 {
1713 if (m_isOrthographic)
1714 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f);
1715 else
1716 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f);
1717 }
1718
1719
1720 // Function to allocate the necessary buffers to render the text. This function is called whenever the buffer size needs to be increased.
1721 void SetMeshArrays(int size)
1722 {
1723 m_textInfo.meshInfo[0].ResizeMeshInfo(size);
1724
1725 m_canvasRenderer.SetMesh(m_textInfo.meshInfo[0].mesh);
1726 }
1727
1728
1729 // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters.
1730 internal override int SetArraySizes(TextProcessingElement[] textProcessingArray)
1731 {
1732 k_SetArraySizesMarker.Begin();
1733
1734 int spriteCount = 0;
1735
1736 m_totalCharacterCount = 0;
1737 m_isUsingBold = false;
1738 m_isTextLayoutPhase = false;
1739 tag_NoParsing = false;
1740 m_FontStyleInternal = m_fontStyle;
1741 m_fontStyleStack.Clear();
1742
1743 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
1744 m_FontWeightStack.SetDefault(m_FontWeightInternal);
1745
1746 m_currentFontAsset = m_fontAsset;
1747 m_currentMaterial = m_sharedMaterial;
1748 m_currentMaterialIndex = 0;
1749
1750 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
1751
1752 m_materialReferenceIndexLookup.Clear();
1753 MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1754
1755 // Set allocations for the text object's TextInfo
1756 if (m_textInfo == null)
1757 m_textInfo = new TMP_TextInfo(m_InternalTextProcessingArraySize);
1758 else if (m_textInfo.characterInfo.Length < m_InternalTextProcessingArraySize)
1759 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_InternalTextProcessingArraySize, false);
1760
1761 m_textElementType = TMP_TextElementType.Character;
1762
1763 // Handling for Underline special character
1764 #region Setup Underline Special Character
1765 /*
1766 GetUnderlineSpecialCharacter(m_currentFontAsset);
1767 if (m_Underline.character != null)
1768 {
1769 if (m_Underline.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
1770 {
1771 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Underline.fontAsset.material.GetInstanceID())
1772 m_Underline.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Underline.fontAsset.material);
1773 else
1774 m_Underline.material = m_Underline.fontAsset.material;
1775
1776 m_Underline.materialIndex = MaterialReference.AddMaterialReference(m_Underline.material, m_Underline.fontAsset, m_materialReferences, m_materialReferenceIndexLookup);
1777 m_materialReferences[m_Underline.materialIndex].referenceCount = 0;
1778 }
1779 }
1780 */
1781 #endregion
1782
1783
1784 // Handling for Ellipsis special character
1785 #region Setup Ellipsis Special Character
1786 if (m_overflowMode == TextOverflowModes.Ellipsis)
1787 {
1788 GetEllipsisSpecialCharacter(m_currentFontAsset);
1789
1790 if (m_Ellipsis.character != null)
1791 {
1792 if (m_Ellipsis.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
1793 {
1794 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Ellipsis.fontAsset.material.GetInstanceID())
1795 m_Ellipsis.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Ellipsis.fontAsset.material);
1796 else
1797 m_Ellipsis.material = m_Ellipsis.fontAsset.material;
1798
1799 m_Ellipsis.materialIndex = MaterialReference.AddMaterialReference(m_Ellipsis.material, m_Ellipsis.fontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1800 m_materialReferences[m_Ellipsis.materialIndex].referenceCount = 0;
1801 }
1802 }
1803 else
1804 {
1805 m_overflowMode = TextOverflowModes.Truncate;
1806
1807 if (!TMP_Settings.warningsDisabled)
1808 Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + m_currentFontAsset.name + "] or any potential fallbacks. Switching Text Overflow mode to Truncate.", this);
1809 }
1810 }
1811 #endregion
1812
1813 // Check if we should process Ligatures
1814 bool ligature = m_ActiveFontFeatures.Contains(OTL_FeatureTag.liga);
1815
1816 // Clear Linked Text object content if we have any.
1817 if (m_overflowMode == TextOverflowModes.Linked && m_linkedTextComponent != null && !m_isCalculatingPreferredValues)
1818 {
1819 TMP_Text linkedComponent = m_linkedTextComponent;
1820
1821 while (linkedComponent != null)
1822 {
1823 linkedComponent.text = String.Empty;
1824 linkedComponent.ClearMesh();
1825 linkedComponent.textInfo.Clear();
1826
1827 linkedComponent = linkedComponent.linkedTextComponent;
1828 }
1829 }
1830
1831
1832 // Parsing XML tags in the text
1833 for (int i = 0; i < textProcessingArray.Length && textProcessingArray[i].unicode != 0; i++)
1834 {
1835 //Make sure the characterInfo array can hold the next text element.
1836 if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length)
1837 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true);
1838
1839 uint unicode = textProcessingArray[i].unicode;
1840
1841 // PARSE XML TAGS
1842 #region PARSE XML TAGS
1843 if (m_isRichText && unicode == 60) // if Char '<'
1844 {
1845 int prev_MaterialIndex = m_currentMaterialIndex;
1846 int endTagIndex;
1847
1848 // Check if Tag is Valid
1849 if (ValidateHtmlTag(textProcessingArray, i + 1, out endTagIndex))
1850 {
1851 int tagStartIndex = textProcessingArray[i].stringIndex;
1852 i = endTagIndex;
1853
1854 if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)
1855 m_isUsingBold = true;
1856
1857 if (m_textElementType == TMP_TextElementType.Sprite)
1858 {
1859 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
1860
1861 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex);
1862 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
1863 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
1864 m_textInfo.characterInfo[m_totalCharacterCount].textElement = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
1865 m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
1866 m_textInfo.characterInfo[m_totalCharacterCount].index = tagStartIndex;
1867 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].stringIndex - tagStartIndex + 1;
1868
1869 // Restore element type and material index to previous values.
1870 m_textElementType = TMP_TextElementType.Character;
1871 m_currentMaterialIndex = prev_MaterialIndex;
1872
1873 spriteCount += 1;
1874 m_totalCharacterCount += 1;
1875 }
1876
1877 continue;
1878 }
1879 }
1880 #endregion
1881
1882 bool isUsingAlternativeTypeface = false;
1883 bool isUsingFallbackOrAlternativeTypeface = false;
1884
1885 TMP_FontAsset prev_fontAsset = m_currentFontAsset;
1886 Material prev_material = m_currentMaterial;
1887 int prev_materialIndex = m_currentMaterialIndex;
1888
1889 // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
1890 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
1891 if (m_textElementType == TMP_TextElementType.Character)
1892 {
1893 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
1894 {
1895 // If this character is lowercase, switch to uppercase.
1896 if (char.IsLower((char)unicode))
1897 unicode = char.ToUpper((char)unicode);
1898
1899 }
1900 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
1901 {
1902 // If this character is uppercase, switch to lowercase.
1903 if (char.IsUpper((char)unicode))
1904 unicode = char.ToLower((char)unicode);
1905 }
1906 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
1907 {
1908 // Only convert lowercase characters to uppercase.
1909 if (char.IsLower((char)unicode))
1910 unicode = char.ToUpper((char)unicode);
1911 }
1912 }
1913 #endregion
1914
1915 // Lookup the Glyph data for each character and cache it.
1916 #region LOOKUP GLYPH
1917 TMP_TextElement character = null;
1918
1919 uint nextCharacter = i + 1 < textProcessingArray.Length ? textProcessingArray[i + 1].unicode : 0;
1920
1921 // Check Emoji Fallback first in the event the requested unicode code point is an Emoji
1922 if (emojiFallbackSupport && ((TMP_TextParsingUtilities.IsEmojiPresentationForm(unicode) && nextCharacter != 0xFE0E) || (TMP_TextParsingUtilities.IsEmoji(unicode) && nextCharacter == 0xFE0F)))
1923 {
1924 if (TMP_Settings.emojiFallbackTextAssets != null && TMP_Settings.emojiFallbackTextAssets.Count > 0)
1925 {
1926 character = TMP_FontAssetUtilities.GetTextElementFromTextAssets(unicode, m_currentFontAsset, TMP_Settings.emojiFallbackTextAssets, true, fontStyle, fontWeight, out isUsingAlternativeTypeface);
1927
1928 if (character != null)
1929 {
1930 // Add character to font asset lookup cache
1931 //fontAsset.AddCharacterToLookupCache(unicode, character);
1932 }
1933 }
1934 }
1935
1936 if (character == null)
1937 character = GetTextElement(unicode, m_currentFontAsset, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1938
1939 // Check if Lowercase or Uppercase variant of the character is available.
1940 /* Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts.
1941 if (glyph == null)
1942 {
1943 if (char.IsLower((char)c))
1944 {
1945 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph))
1946 c = chars[i] = char.ToUpper((char)c);
1947 }
1948 else if (char.IsUpper((char)c))
1949 {
1950 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph))
1951 c = chars[i] = char.ToLower((char)c);
1952 }
1953 }*/
1954
1955 #region MISSING CHARACTER HANDLING
1956 // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph.
1957 if (character == null)
1958 {
1959 DoMissingGlyphCallback((int)unicode, textProcessingArray[i].stringIndex, m_currentFontAsset);
1960
1961 // Save the original unicode character
1962 uint srcGlyph = unicode;
1963
1964 // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character.
1965 unicode = textProcessingArray[i].unicode = (uint)TMP_Settings.missingGlyphCharacter == 0 ? 9633 : (uint)TMP_Settings.missingGlyphCharacter;
1966
1967 // Check for the missing glyph character in the currently assigned font asset and its fallbacks
1968 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1969
1970 if (character == null)
1971 {
1972 // Search for the missing glyph character in the TMP Settings Fallback list.
1973 if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
1974 character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1975 }
1976
1977 if (character == null)
1978 {
1979 // Search for the missing glyph in the TMP Settings Default Font Asset.
1980 if (TMP_Settings.defaultFontAsset != null)
1981 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1982 }
1983
1984 if (character == null)
1985 {
1986 // Use Space (32) Glyph from the currently assigned font asset.
1987 unicode = textProcessingArray[i].unicode = 32;
1988 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1989 }
1990
1991 if (character == null)
1992 {
1993 // Use End of Text (0x03) Glyph from the currently assigned font asset.
1994 unicode = textProcessingArray[i].unicode = 0x03;
1995 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1996 }
1997
1998 if (!TMP_Settings.warningsDisabled)
1999 {
2000 string formattedWarning = srcGlyph > 0xFFFF
2001 ? string.Format("The character with Unicode value \\U{0:X8} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name)
2002 : string.Format("The character with Unicode value \\u{0:X4} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name);
2003
2004 Debug.LogWarning(formattedWarning, this);
2005 }
2006 }
2007 #endregion
2008
2009 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = null;
2010
2011 if (character.elementType == TextElementType.Character)
2012 {
2013 if (character.textAsset.instanceID != m_currentFontAsset.instanceID)
2014 {
2015 isUsingFallbackOrAlternativeTypeface = true;
2016 m_currentFontAsset = character.textAsset as TMP_FontAsset;
2017 }
2018
2019 #region VARIATION SELECTOR
2020 if (nextCharacter >= 0xFE00 && nextCharacter <= 0xFE0F || nextCharacter >= 0xE0100 && nextCharacter <= 0xE01EF)
2021 {
2022 // Get potential variant glyph index
2023 uint variantGlyphIndex = m_currentFontAsset.GetGlyphVariantIndex((uint)unicode, nextCharacter);
2024
2025 if (variantGlyphIndex != 0)
2026 {
2027 if (m_currentFontAsset.TryAddGlyphInternal(variantGlyphIndex, out Glyph glyph))
2028 {
2029 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph;
2030 }
2031 }
2032
2033 textProcessingArray[i + 1].unicode = 0x1A;
2034 i += 1;
2035 }
2036 #endregion
2037
2038 #region LIGATURES
2039 if (ligature && m_currentFontAsset.fontFeatureTable.m_LigatureSubstitutionRecordLookup.TryGetValue(character.glyphIndex, out List<LigatureSubstitutionRecord> records))
2040 {
2041 if (records == null)
2042 break;
2043
2044 for (int j = 0; j < records.Count; j++)
2045 {
2046 LigatureSubstitutionRecord record = records[j];
2047
2048 int componentCount = record.componentGlyphIDs.Length;
2049 uint ligatureGlyphID = record.ligatureGlyphID;
2050
2051 //
2052 for (int k = 1; k < componentCount; k++)
2053 {
2054 uint componentUnicode = (uint)textProcessingArray[i + k].unicode;
2055
2056 // Special Handling for Zero Width Joiner (ZWJ)
2057 //if (componentUnicode == 0x200D)
2058 // continue;
2059
2060 uint glyphIndex = m_currentFontAsset.GetGlyphIndex(componentUnicode);
2061
2062 if (glyphIndex == record.componentGlyphIDs[k])
2063 continue;
2064
2065 ligatureGlyphID = 0;
2066 break;
2067 }
2068
2069 if (ligatureGlyphID != 0)
2070 {
2071 if (m_currentFontAsset.TryAddGlyphInternal(ligatureGlyphID, out Glyph glyph))
2072 {
2073 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph;
2074
2075 // Update text processing array
2076 for (int c = 0; c < componentCount; c++)
2077 {
2078 if (c == 0)
2079 {
2080 textProcessingArray[i + c].length = componentCount;
2081 continue;
2082 }
2083
2084 textProcessingArray[i + c].unicode = 0x1A;
2085 }
2086
2087 i += componentCount - 1;
2088 break;
2089 }
2090 }
2091 }
2092 }
2093 #endregion
2094 }
2095 #endregion
2096
2097 // Save text element data
2098 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character;
2099 m_textInfo.characterInfo[m_totalCharacterCount].textElement = character;
2100 m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface;
2101 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
2102 m_textInfo.characterInfo[m_totalCharacterCount].index = textProcessingArray[i].stringIndex;
2103 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].length;
2104 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
2105
2106 // Special handling if the character is a sprite.
2107 if (character.elementType == TextElementType.Sprite)
2108 {
2109 TMP_SpriteAsset spriteAssetRef = character.textAsset as TMP_SpriteAsset;
2110 m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAssetRef.material, spriteAssetRef, ref m_materialReferences, m_materialReferenceIndexLookup);
2111 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
2112
2113 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Sprite;
2114 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
2115
2116 // Restore element type and material index to previous values.
2117 m_textElementType = TMP_TextElementType.Character;
2118 m_currentMaterialIndex = prev_materialIndex;
2119
2120 spriteCount += 1;
2121 m_totalCharacterCount += 1;
2122
2123 continue;
2124 }
2125
2126 if (isUsingFallbackOrAlternativeTypeface && m_currentFontAsset.instanceID != m_fontAsset.instanceID)
2127 {
2128 // Create Fallback material instance matching current material preset if necessary
2129 if (TMP_Settings.matchMaterialPreset)
2130 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material);
2131 else
2132 m_currentMaterial = m_currentFontAsset.material;
2133
2134 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
2135 }
2136
2137 // Handle Multi Atlas Texture support
2138 if (character != null && character.glyph.atlasIndex > 0)
2139 {
2140 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentFontAsset, m_currentMaterial, character.glyph.atlasIndex);
2141
2142 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
2143
2144 isUsingFallbackOrAlternativeTypeface = true;
2145 }
2146
2147 if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B)
2148 {
2149 // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
2150 if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
2151 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
2152 else
2153 {
2154 m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
2155 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
2156 }
2157 }
2158
2159 m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial;
2160 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
2161 m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallbackOrAlternativeTypeface;
2162
2163 // Restore previous font asset and material if fallback font was used.
2164 if (isUsingFallbackOrAlternativeTypeface)
2165 {
2166 m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material;
2167 m_currentFontAsset = prev_fontAsset;
2168 m_currentMaterial = prev_material;
2169 m_currentMaterialIndex = prev_materialIndex;
2170 }
2171
2172 m_totalCharacterCount += 1;
2173 }
2174
2175 // Early return if we are calculating the preferred values.
2176 if (m_isCalculatingPreferredValues)
2177 {
2178 m_isCalculatingPreferredValues = false;
2179
2180 k_SetArraySizesMarker.End();
2181 return m_totalCharacterCount;
2182 }
2183
2184 // Save material and sprite count.
2185 m_textInfo.spriteCount = spriteCount;
2186 int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count;
2187
2188 // Check if we need to resize the MeshInfo array for handling different materials.
2189 if (materialCount > m_textInfo.meshInfo.Length)
2190 TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false);
2191
2192 // Resize SubTextObject array if necessary
2193 if (materialCount > m_subTextObjects.Length)
2194 TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1));
2195
2196 // Resize CharacterInfo[] if allocations are excessive
2197 if (m_VertexBufferAutoSizeReduction && m_textInfo.characterInfo.Length - m_totalCharacterCount > 256)
2198 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true);
2199
2200
2201 // Iterate through the material references to set the mesh buffer allocations
2202 for (int i = 0; i < materialCount; i++)
2203 {
2204 // Add new sub text object for each material reference
2205 if (i > 0)
2206 {
2207 if (m_subTextObjects[i] == null)
2208 {
2209 m_subTextObjects[i] = TMP_SubMeshUI.AddSubTextObject(this, m_materialReferences[i]);
2210
2211 // Not sure this is necessary
2212 m_textInfo.meshInfo[i].vertices = null;
2213 }
2214 //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false)
2215 // m_subTextObjects[i].gameObject.SetActive(true);
2216
2217 // Make sure the pivots are synchronized
2218 if (m_rectTransform.pivot != m_subTextObjects[i].rectTransform.pivot)
2219 m_subTextObjects[i].rectTransform.pivot = m_rectTransform.pivot;
2220
2221 // Check if the material has changed.
2222 if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID())
2223 {
2224 m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material;
2225 m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset;
2226 m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset;
2227 }
2228
2229 // Check if we need to use a Fallback Material
2230 if (m_materialReferences[i].isFallbackMaterial)
2231 {
2232 m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material;
2233 m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial;
2234 }
2235 }
2236
2237 int referenceCount = m_materialReferences[i].referenceCount;
2238
2239 // Check to make sure our buffers allocations can accommodate the required text elements.
2240 if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * 4)
2241 {
2242 if (m_textInfo.meshInfo[i].vertices == null)
2243 {
2244 if (i == 0)
2245 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1);
2246 else
2247 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1);
2248 }
2249 else
2250 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
2251 }
2252 else if (m_VertexBufferAutoSizeReduction && referenceCount > 0 && m_textInfo.meshInfo[i].vertices.Length / 4 - referenceCount > 256)
2253 {
2254 // Resize vertex buffers if allocations are excessive.
2255 //Debug.Log("Reducing the size of the vertex buffers.");
2256 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
2257 }
2258
2259 // Assign material reference
2260 m_textInfo.meshInfo[i].material = m_materialReferences[i].material;
2261 }
2262
2263 //TMP_MaterialManager.CleanupFallbackMaterials();
2264
2265 // Clean up unused SubMeshes
2266 for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
2267 {
2268 if (i < m_textInfo.meshInfo.Length)
2269 {
2270 m_subTextObjects[i].canvasRenderer.SetMesh(null);
2271
2272 // TODO: Figure out a way to handle this without running into Unity's Rebuild loop issue.
2273 //m_subTextObjects[i].gameObject.SetActive(false);
2274 }
2275 }
2276
2277 k_SetArraySizesMarker.End();
2278 return m_totalCharacterCount;
2279 }
2280
2281
2282 // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera.
2283 //void OnBecameInvisible()
2284 //{
2285 // if (m_mesh != null)
2286 // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
2287 //}
2288
2289
2290 /// <summary>
2291 /// Update the margin width and height
2292 /// </summary>
2293 public override void ComputeMarginSize()
2294 {
2295 if (this.rectTransform != null)
2296 {
2297 //Debug.Log("*** ComputeMarginSize() *** Current RectTransform's Width is " + m_rectTransform.rect.width + " and Height is " + m_rectTransform.rect.height); // + " and size delta is " + m_rectTransform.sizeDelta);
2298 Rect rect = m_rectTransform.rect;
2299
2300 m_marginWidth = rect.width - m_margin.x - m_margin.z;
2301 m_marginHeight = rect.height - m_margin.y - m_margin.w;
2302
2303 // Cache current RectTransform width and pivot referenced in OnRectTransformDimensionsChange() to get around potential rounding error in the reported width of the RectTransform.
2304 m_PreviousRectTransformSize = rect.size;
2305 m_PreviousPivotPosition = m_rectTransform.pivot;
2306
2307 // Update the corners of the RectTransform
2308 m_RectTransformCorners = GetTextContainerLocalCorners();
2309 }
2310 }
2311
2312
2313 /// <summary>
2314 ///
2315 /// </summary>
2316 protected override void OnDidApplyAnimationProperties()
2317 {
2318 m_havePropertiesChanged = true;
2319 SetVerticesDirty();
2320 SetLayoutDirty();
2321 //Debug.Log("Animation Properties have changed.");
2322 }
2323
2324
2325 protected override void OnCanvasHierarchyChanged()
2326 {
2327 base.OnCanvasHierarchyChanged();
2328
2329 m_canvas = canvas;
2330
2331 if (!m_isAwake || !isActiveAndEnabled)
2332 return;
2333
2334 // Special handling to stop InternalUpdate calls when parent Canvas is disabled.
2335 if (m_canvas == null || m_canvas.enabled == false)
2336 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
2337 else if (m_IsTextObjectScaleStatic == false)
2338 TMP_UpdateManager.RegisterTextObjectForUpdate(this);
2339 }
2340
2341
2342 protected override void OnTransformParentChanged()
2343 {
2344 //Debug.Log("***** OnTransformParentChanged *****");
2345
2346 base.OnTransformParentChanged();
2347
2348 m_canvas = this.canvas;
2349
2350 ComputeMarginSize();
2351 m_havePropertiesChanged = true;
2352 }
2353
2354
2355 protected override void OnRectTransformDimensionsChange()
2356 {
2357 //Debug.Log("*** OnRectTransformDimensionsChange() *** ActiveInHierarchy: " + this.gameObject.activeInHierarchy + " Frame: " + Time.frameCount);
2358
2359 // Make sure object is active in Hierarchy
2360 if (!this.gameObject.activeInHierarchy)
2361 return;
2362
2363 // Check if Canvas scale factor has changed as this requires an update of the SDF Scale.
2364 bool hasCanvasScaleFactorChanged = false;
2365 if (m_canvas != null && m_CanvasScaleFactor != m_canvas.scaleFactor)
2366 {
2367 m_CanvasScaleFactor = m_canvas.scaleFactor;
2368 hasCanvasScaleFactorChanged = true;
2369 }
2370
2371 // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode.
2372 if (hasCanvasScaleFactorChanged == false &&
2373 rectTransform != null &&
2374 Mathf.Abs(m_rectTransform.rect.width - m_PreviousRectTransformSize.x) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_PreviousRectTransformSize.y) < 0.0001f &&
2375 Mathf.Abs(m_rectTransform.pivot.x - m_PreviousPivotPosition.x) < 0.0001f && Mathf.Abs(m_rectTransform.pivot.y - m_PreviousPivotPosition.y) < 0.0001f)
2376 {
2377 return;
2378 }
2379
2380 ComputeMarginSize();
2381
2382 UpdateSubObjectPivot();
2383
2384 SetVerticesDirty();
2385 SetLayoutDirty();
2386 }
2387
2388
2389 /// <summary>
2390 /// Function used as a replacement for LateUpdate to check if the transform or scale of the text object has changed.
2391 /// </summary>
2392 internal override void InternalUpdate()
2393 {
2394 // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed.
2395 if (m_havePropertiesChanged == false)
2396 {
2397 float lossyScaleY = m_rectTransform.lossyScale.y;
2398
2399 if (lossyScaleY != m_previousLossyScaleY && m_TextProcessingArray[0].unicode != 0)
2400 {
2401 float scaleDelta = lossyScaleY / m_previousLossyScaleY;
2402
2403 // Only update SDF Scale when lossy scale has changed by more than 20%
2404 if (scaleDelta < 0.8f || scaleDelta > 1.25f)
2405 {
2406 UpdateSDFScale(scaleDelta);
2407 m_previousLossyScaleY = lossyScaleY;
2408 }
2409 }
2410 }
2411
2412 // Added to handle legacy animation mode.
2413 if (m_isUsingLegacyAnimationComponent)
2414 {
2415 m_havePropertiesChanged = true;
2416 OnPreRenderCanvas();
2417 }
2418
2419 // Update Environment Matrix property to support changing the rotation via a script.
2420 UpdateEnvMapMatrix();
2421 }
2422
2423
2424 /// <summary>
2425 /// Function called when the text needs to be updated.
2426 /// </summary>
2427 void OnPreRenderCanvas()
2428 {
2429 //Debug.Log("*** OnPreRenderCanvas() *** Frame: " + Time.frameCount);
2430
2431 // Make sure object is active and that we have a valid Canvas.
2432 if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false))
2433 return;
2434
2435 if (m_canvas == null) { m_canvas = this.canvas; if (m_canvas == null) return; }
2436
2437
2438 if (m_havePropertiesChanged || m_isLayoutDirty)
2439 {
2440 //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + ".");
2441
2442 // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen.
2443 if (m_fontAsset == null)
2444 {
2445 Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this);
2446 return;
2447 }
2448
2449 // Update mesh padding if necessary.
2450 if (checkPaddingRequired)
2451 UpdateMeshPadding();
2452
2453 // Reparse the text as input may have changed or been truncated.
2454 ParseInputText();
2455 TMP_FontAsset.UpdateFontAssetsInUpdateQueue();
2456
2457 // Reset Font min / max used with Auto-sizing
2458 if (m_enableAutoSizing)
2459 m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax);
2460
2461 m_maxFontSize = m_fontSizeMax;
2462 m_minFontSize = m_fontSizeMin;
2463 m_lineSpacingDelta = 0;
2464 m_charWidthAdjDelta = 0;
2465
2466 m_isTextTruncated = false;
2467
2468 m_havePropertiesChanged = false;
2469 m_isLayoutDirty = false;
2470 m_ignoreActiveState = false;
2471
2472 // Reset Text Auto Size iteration tracking.
2473 m_IsAutoSizePointSizeSet = false;
2474 m_AutoSizeIterationCount = 0;
2475
2476 // The GenerateTextMesh function is potentially called repeatedly when text auto size is enabled.
2477 // This is a revised implementation to remove the use of recursion which could potentially result in stack overflow issues.
2478 while (m_IsAutoSizePointSizeSet == false)
2479 {
2480 GenerateTextMesh();
2481 m_AutoSizeIterationCount += 1;
2482 }
2483 }
2484 }
2485
2486
2487 /// <summary>
2488 /// This is the main function that is responsible for creating / displaying the text.
2489 /// </summary>
2490 protected virtual void GenerateTextMesh()
2491 {
2492 k_GenerateTextMarker.Begin();
2493
2494 // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
2495 if (m_fontAsset == null || m_fontAsset.characterLookupTable == null)
2496 {
2497 Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
2498 m_IsAutoSizePointSizeSet = true;
2499 k_GenerateTextMarker.End();
2500 return;
2501 }
2502
2503 // Clear TextInfo
2504 if (m_textInfo != null)
2505 m_textInfo.Clear();
2506
2507 // Early exit if we don't have any Text to generate.
2508 if (m_TextProcessingArray == null || m_TextProcessingArray.Length == 0 || m_TextProcessingArray[0].unicode == 0)
2509 {
2510 // Clear mesh and upload changes to the mesh.
2511 ClearMesh();
2512
2513 m_preferredWidth = 0;
2514 m_preferredHeight = 0;
2515
2516 // Event indicating the text has been regenerated.
2517 TMPro_EventManager.ON_TEXT_CHANGED(this);
2518 m_IsAutoSizePointSizeSet = true;
2519 k_GenerateTextMarker.End();
2520 return;
2521 }
2522
2523 m_currentFontAsset = m_fontAsset;
2524 m_currentMaterial = m_sharedMaterial;
2525 m_currentMaterialIndex = 0;
2526 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
2527
2528 m_currentSpriteAsset = m_spriteAsset;
2529
2530 // Stop all Sprite Animations
2531 if (m_spriteAnimator != null)
2532 m_spriteAnimator.StopAllAnimations();
2533
2534 // Total character count is computed when the text is parsed.
2535 int totalCharacterCount = m_totalCharacterCount;
2536
2537 // Calculate the scale of the font based on selected font size and sampling point size.
2538 // baseScale is calculated using the font asset assigned to the text object.
2539 float baseScale = (m_fontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
2540 float currentElementScale = baseScale;
2541 float currentEmScale = m_fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f);
2542 m_fontScaleMultiplier = 1;
2543
2544 m_currentFontSize = m_fontSize;
2545 m_sizeStack.SetDefault(m_currentFontSize);
2546 float fontSizeDelta = 0;
2547
2548 uint charCode = 0; // Holds the character code of the currently being processed character.
2549
2550 m_FontStyleInternal = m_fontStyle; // Set the default style.
2551 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
2552 m_FontWeightStack.SetDefault(m_FontWeightInternal);
2553 m_fontStyleStack.Clear();
2554
2555 m_lineJustification = m_HorizontalAlignment; // m_textAlignment; // Sets the line justification mode to match editor alignment.
2556 m_lineJustificationStack.SetDefault(m_lineJustification);
2557
2558 float padding = 0;
2559
2560 m_baselineOffset = 0; // Used by subscript characters.
2561 m_baselineOffsetStack.Clear();
2562
2563 // Underline
2564 bool beginUnderline = false;
2565 Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends.
2566 Vector3 underline_end = Vector3.zero;
2567
2568 // Strike-through
2569 bool beginStrikethrough = false;
2570 Vector3 strikethrough_start = Vector3.zero;
2571 Vector3 strikethrough_end = Vector3.zero;
2572
2573 // Text Highlight
2574 bool beginHighlight = false;
2575 Vector3 highlight_start = Vector3.zero;
2576 Vector3 highlight_end = Vector3.zero;
2577
2578 m_fontColor32 = m_fontColor;
2579 m_htmlColor = m_fontColor32;
2580 m_underlineColor = m_htmlColor;
2581 m_strikethroughColor = m_htmlColor;
2582
2583 m_colorStack.SetDefault(m_htmlColor);
2584 m_underlineColorStack.SetDefault(m_htmlColor);
2585 m_strikethroughColorStack.SetDefault(m_htmlColor);
2586 m_HighlightStateStack.SetDefault(new HighlightState(m_htmlColor, TMP_Offset.zero));
2587
2588 m_colorGradientPreset = null;
2589 m_colorGradientStack.SetDefault(null);
2590
2591 m_ItalicAngle = m_currentFontAsset.italicStyle;
2592 m_ItalicAngleStack.SetDefault(m_ItalicAngle);
2593
2594 // Clear the Style stack.
2595 //m_styleStack.Clear();
2596
2597 // Clear the Action stack.
2598 m_actionStack.Clear();
2599
2600 m_FXScale = Vector3.one;
2601 m_FXRotation = Quaternion.identity;
2602
2603 m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
2604 m_lineHeight = TMP_Math.FLOAT_UNSET;
2605 float lineGap = m_currentFontAsset.m_FaceInfo.lineHeight - (m_currentFontAsset.m_FaceInfo.ascentLine - m_currentFontAsset.m_FaceInfo.descentLine);
2606
2607 m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
2608 m_monoSpacing = 0;
2609 m_xAdvance = 0; // Used to track the position of each character.
2610
2611 tag_LineIndent = 0; // Used for indentation of text.
2612 tag_Indent = 0;
2613 m_indentStack.SetDefault(0);
2614 tag_NoParsing = false;
2615 //m_isIgnoringAlignment = false;
2616
2617 m_characterCount = 0; // Total characters in the char[]
2618
2619 // Tracking of line information
2620 m_firstCharacterOfLine = m_firstVisibleCharacter;
2621 m_lastCharacterOfLine = 0;
2622 m_firstVisibleCharacterOfLine = 0;
2623 m_lastVisibleCharacterOfLine = 0;
2624 m_maxLineAscender = k_LargeNegativeFloat;
2625 m_maxLineDescender = k_LargePositiveFloat;
2626 m_lineNumber = 0;
2627 m_startOfLineAscender = 0;
2628 m_startOfLineDescender = 0;
2629 m_lineVisibleCharacterCount = 0;
2630 m_lineVisibleSpaceCount = 0;
2631 bool isStartOfNewLine = true;
2632 m_IsDrivenLineSpacing = false;
2633 m_firstOverflowCharacterIndex = -1;
2634 m_LastBaseGlyphIndex = int.MinValue;
2635
2636 bool kerning = m_ActiveFontFeatures.Contains(OTL_FeatureTag.kern);
2637 bool markToBase = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mark);
2638 bool markToMark = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mkmk);
2639
2640 m_pageNumber = 0;
2641 int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1);
2642 m_textInfo.ClearPageInfo();
2643
2644 Vector4 margins = m_margin;
2645 float marginWidth = m_marginWidth > 0 ? m_marginWidth : 0;
2646 float marginHeight = m_marginHeight > 0 ? m_marginHeight : 0;
2647 m_marginLeft = 0;
2648 m_marginRight = 0;
2649 m_width = -1;
2650 float widthOfTextArea = marginWidth + 0.0001f - m_marginLeft - m_marginRight;
2651
2652 // Need to initialize these Extents structures
2653 m_meshExtents.min = k_LargePositiveVector2;
2654 m_meshExtents.max = k_LargeNegativeVector2;
2655
2656 // Initialize lineInfo
2657 m_textInfo.ClearLineInfo();
2658
2659 // Tracking of the highest Ascender
2660 m_maxCapHeight = 0;
2661 m_maxTextAscender = 0;
2662 m_ElementDescender = 0;
2663 m_PageAscender = 0;
2664 float maxVisibleDescender = 0;
2665 bool isMaxVisibleDescenderSet = false;
2666 m_isNewPage = false;
2667
2668 // Initialize struct to track states of word wrapping
2669 bool isFirstWordOfLine = true;
2670 m_isNonBreakingSpace = false;
2671 bool ignoreNonBreakingSpace = false;
2672 int lastSoftLineBreak = 0;
2673
2674 CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0);
2675 bool isSoftHyphenIgnored = false;
2676
2677 // Save character and line state before we begin layout.
2678 SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1);
2679 SaveWordWrappingState(ref m_SavedLineState, -1, -1);
2680 SaveWordWrappingState(ref m_SavedEllipsisState, -1, -1);
2681 SaveWordWrappingState(ref m_SavedLastValidState, -1, -1);
2682 SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1);
2683
2684 m_EllipsisInsertionCandidateStack.Clear();
2685
2686 // Safety Tracker
2687 int restoreCount = 0;
2688
2689 k_GenerateTextPhaseIMarker.Begin();
2690
2691 // Parse through Character buffer to read HTML tags and begin creating mesh.
2692 for (int i = 0; i < m_TextProcessingArray.Length && m_TextProcessingArray[i].unicode != 0; i++)
2693 {
2694 charCode = m_TextProcessingArray[i].unicode;
2695
2696 if (restoreCount > 5)
2697 {
2698 Debug.LogError("Line breaking recursion max threshold hit... Character [" + charCode + "] index: " + i);
2699 characterToSubstitute.index = m_characterCount;
2700 characterToSubstitute.unicode = 0x03;
2701 }
2702
2703 // Skip characters that have been substituted.
2704 if (charCode == 0x1A)
2705 continue;
2706
2707 // Parse Rich Text Tag
2708 #region Parse Rich Text Tag
2709 if (m_isRichText && charCode == '<')
2710 {
2711 k_ParseMarkupTextMarker.Begin();
2712
2713 m_isTextLayoutPhase = true;
2714 m_textElementType = TMP_TextElementType.Character;
2715 int endTagIndex;
2716
2717 // Check if Tag is valid. If valid, skip to the end of the validated tag.
2718 if (ValidateHtmlTag(m_TextProcessingArray, i + 1, out endTagIndex))
2719 {
2720 i = endTagIndex;
2721
2722 // Continue to next character or handle the sprite element
2723 if (m_textElementType == TMP_TextElementType.Character)
2724 {
2725 k_ParseMarkupTextMarker.End();
2726 continue;
2727 }
2728 }
2729 k_ParseMarkupTextMarker.End();
2730 }
2731 else
2732 {
2733 m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
2734 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
2735 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
2736 }
2737 #endregion End Parse Rich Text Tag
2738
2739 int previousMaterialIndex = m_currentMaterialIndex;
2740 bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
2741
2742 m_isTextLayoutPhase = false;
2743
2744 // Handle potential character substitutions
2745 #region Character Substitutions
2746 bool isInjectedCharacter = false;
2747
2748 if (characterToSubstitute.index == m_characterCount)
2749 {
2750 charCode = characterToSubstitute.unicode;
2751 m_textElementType = TMP_TextElementType.Character;
2752 isInjectedCharacter = true;
2753
2754 switch (charCode)
2755 {
2756 case 0x03:
2757 m_textInfo.characterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03];
2758 m_isTextTruncated = true;
2759 break;
2760 case 0x2D:
2761 //
2762 break;
2763 case 0x2026:
2764 m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character;
2765 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
2766 m_textInfo.characterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset;
2767 m_textInfo.characterInfo[m_characterCount].material = m_Ellipsis.material;
2768 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex;
2769
2770 // Need to increase reference count in the event the primary mesh has no characters.
2771 m_materialReferences[m_Underline.materialIndex].referenceCount += 1;
2772
2773 // Indicates the source parsing data has been modified.
2774 m_isTextTruncated = true;
2775
2776 // End Of Text
2777 characterToSubstitute.index = m_characterCount + 1;
2778 characterToSubstitute.unicode = 0x03;
2779 break;
2780 }
2781 }
2782 #endregion
2783
2784
2785 // When using Linked text, mark character as ignored and skip to next character.
2786 #region Linked Text
2787 if (m_characterCount < m_firstVisibleCharacter && charCode != 0x03)
2788 {
2789 m_textInfo.characterInfo[m_characterCount].isVisible = false;
2790 m_textInfo.characterInfo[m_characterCount].character = (char)0x200B;
2791 m_textInfo.characterInfo[m_characterCount].lineNumber = 0;
2792 m_characterCount += 1;
2793 continue;
2794 }
2795 #endregion
2796
2797
2798 // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
2799 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
2800
2801 float smallCapsMultiplier = 1.0f;
2802
2803 if (m_textElementType == TMP_TextElementType.Character)
2804 {
2805 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
2806 {
2807 // If this character is lowercase, switch to uppercase.
2808 if (char.IsLower((char)charCode))
2809 charCode = char.ToUpper((char)charCode);
2810
2811 }
2812 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
2813 {
2814 // If this character is uppercase, switch to lowercase.
2815 if (char.IsUpper((char)charCode))
2816 charCode = char.ToLower((char)charCode);
2817 }
2818 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
2819 {
2820 if (char.IsLower((char)charCode))
2821 {
2822 smallCapsMultiplier = 0.8f;
2823 charCode = char.ToUpper((char)charCode);
2824 }
2825 }
2826 }
2827 #endregion
2828
2829
2830 // Look up Character Data from Dictionary and cache it.
2831 #region Look up Character Data
2832 k_CharacterLookupMarker.Begin();
2833
2834 float baselineOffset = 0;
2835 float elementAscentLine = 0;
2836 float elementDescentLine = 0;
2837 if (m_textElementType == TMP_TextElementType.Sprite)
2838 {
2839 // If a sprite is used as a fallback then get a reference to it and set the color to white.
2840 TMP_SpriteCharacter sprite = (TMP_SpriteCharacter)textInfo.characterInfo[m_characterCount].textElement;
2841 m_currentSpriteAsset = sprite.textAsset as TMP_SpriteAsset;
2842 m_spriteIndex = (int)sprite.glyphIndex;
2843
2844 if (sprite == null)
2845 {
2846 k_CharacterLookupMarker.End();
2847 continue;
2848 }
2849
2850 // Sprites are assigned in the E000 Private Area + sprite Index
2851 if (charCode == '<')
2852 charCode = 57344 + (uint)m_spriteIndex;
2853 else
2854 m_spriteColor = s_colorWhite;
2855
2856 float fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
2857
2858 // The sprite scale calculations are based on the font asset assigned to the text object.
2859 if (m_currentSpriteAsset.m_FaceInfo.pointSize > 0)
2860 {
2861 float spriteScale = m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2862 currentElementScale = sprite.m_Scale * sprite.m_Glyph.scale * spriteScale;
2863 elementAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine;
2864 baselineOffset = m_currentSpriteAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.m_FaceInfo.scale;
2865 elementDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine;
2866 }
2867 else
2868 {
2869 float spriteScale = m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2870 currentElementScale = m_currentFontAsset.m_FaceInfo.ascentLine / sprite.m_Glyph.metrics.height * sprite.m_Scale * sprite.m_Glyph.scale * spriteScale;
2871 float scaleDelta = spriteScale / currentElementScale;
2872 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine * scaleDelta;
2873 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
2874 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine * scaleDelta;
2875 }
2876
2877 m_cached_TextElement = sprite;
2878
2879 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
2880 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
2881 m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset;
2882 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
2883
2884 m_currentMaterialIndex = previousMaterialIndex;
2885
2886 padding = 0;
2887 }
2888 else if (m_textElementType == TMP_TextElementType.Character)
2889 {
2890 m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
2891 if (m_cached_TextElement == null)
2892 {
2893 k_CharacterLookupMarker.End();
2894 continue;
2895 }
2896
2897 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
2898 m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
2899 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
2900
2901 // Special handling if replaced character was a line feed where in this case we have to use the scale of the previous character.
2902 float adjustedScale;
2903 if (isInjectedCharacter && m_TextProcessingArray[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine)
2904 adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2905 else
2906 adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2907
2908 // Special handling for injected Ellipsis
2909 if (isInjectedCharacter && charCode == 0x2026)
2910 {
2911 elementAscentLine = 0;
2912 elementDescentLine = 0;
2913 }
2914 else
2915 {
2916 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine;
2917 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine;
2918 }
2919
2920 currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale;
2921 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * adjustedScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
2922
2923 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
2924 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
2925
2926 padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding;
2927 }
2928 k_CharacterLookupMarker.End();
2929 #endregion
2930
2931
2932 // Handle Soft Hyphen
2933 #region Handle Soft Hyphen
2934 float currentElementUnmodifiedScale = currentElementScale;
2935 if (charCode == 0xAD || charCode == 0x03)
2936 currentElementScale = 0;
2937 #endregion
2938
2939
2940 // Store some of the text object's information
2941 m_textInfo.characterInfo[m_characterCount].character = (char)charCode;
2942 m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize;
2943 m_textInfo.characterInfo[m_characterCount].color = m_htmlColor;
2944 m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor;
2945 m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor;
2946 m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightState;
2947 m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal;
2948
2949 // Cache glyph metrics
2950 Glyph altGlyph = m_textInfo.characterInfo[m_characterCount].alternativeGlyph;
2951 GlyphMetrics currentGlyphMetrics = altGlyph == null ? m_cached_TextElement.m_Glyph.metrics : altGlyph.metrics;
2952
2953 // Optimization to avoid calling this more than once per character.
2954 bool isWhiteSpace = charCode <= 0xFFFF && char.IsWhiteSpace((char)charCode);
2955
2956 // Handle Kerning if Enabled.
2957 #region Handle Kerning
2958 GlyphValueRecord glyphAdjustments = new GlyphValueRecord();
2959 float characterSpacingAdjustment = m_characterSpacing;
2960 if (kerning && m_textElementType == TMP_TextElementType.Character)
2961 {
2962 k_HandleGPOSFeaturesMarker.Begin();
2963
2964 GlyphPairAdjustmentRecord adjustmentPair;
2965 uint baseGlyphIndex = m_cached_TextElement.m_GlyphIndex;
2966
2967 if (m_characterCount < totalCharacterCount - 1 && textInfo.characterInfo[m_characterCount + 1].elementType == TMP_TextElementType.Character)
2968 {
2969 uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex;
2970 uint key = nextGlyphIndex << 16 | baseGlyphIndex;
2971
2972 if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair))
2973 {
2974 glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord;
2975 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
2976 }
2977 }
2978
2979 if (m_characterCount >= 1)
2980 {
2981 uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex;
2982 uint key = baseGlyphIndex << 16 | previousGlyphIndex;
2983
2984 if (textInfo.characterInfo[m_characterCount - 1].elementType == TMP_TextElementType.Character && m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair))
2985 {
2986 glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord;
2987 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
2988 }
2989 }
2990
2991 k_HandleGPOSFeaturesMarker.End();
2992 }
2993
2994 m_textInfo.characterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.xAdvance;
2995 #endregion
2996
2997
2998 // Handle Diacritical Marks
2999 #region Handle Diacritical Marks
3000 bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph(charCode);
3001
3002 if (isBaseGlyph)
3003 m_LastBaseGlyphIndex = m_characterCount;
3004
3005 if (m_characterCount > 0 && !isBaseGlyph)
3006 {
3007 // Check for potential Mark-to-Base lookup if previous glyph was a base glyph
3008 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && m_LastBaseGlyphIndex == m_characterCount - 1)
3009 {
3010 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph;
3011 uint baseGlyphIndex = baseGlyph.index;
3012 uint markGlyphIndex = m_cached_TextElement.glyphIndex;
3013 uint key = markGlyphIndex << 16 | baseGlyphIndex;
3014
3015 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord))
3016 {
3017 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale;
3018
3019 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment;
3020 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment;
3021
3022 characterSpacingAdjustment = 0;
3023 }
3024 }
3025 else
3026 {
3027 // Iterate from previous glyph to last base glyph checking for any potential Mark-to-Mark lookups to apply. Otherwise check for potential Mark-to-Base lookup between the current glyph and last base glyph
3028 bool wasLookupApplied = false;
3029
3030 // Check for any potential Mark-to-Mark lookups
3031 if (markToMark)
3032 {
3033 for (int characterLookupIndex = m_characterCount - 1; characterLookupIndex >= 0 && characterLookupIndex != m_LastBaseGlyphIndex; characterLookupIndex--)
3034 {
3035 // Handle any potential Mark-to-Mark lookup
3036 Glyph baseMarkGlyph = m_textInfo.characterInfo[characterLookupIndex].textElement.glyph;
3037 uint baseGlyphIndex = baseMarkGlyph.index;
3038 uint combiningMarkGlyphIndex = m_cached_TextElement.glyphIndex;
3039 uint key = combiningMarkGlyphIndex << 16 | baseGlyphIndex;
3040
3041 if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out MarkToMarkAdjustmentRecord glyphAdjustmentRecord))
3042 {
3043 float baseMarkOrigin = (m_textInfo.characterInfo[characterLookupIndex].origin - m_xAdvance) / currentElementScale;
3044 float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset;
3045 float baseMarkBaseline = (m_textInfo.characterInfo[characterLookupIndex].baseLine - currentBaseline) / currentElementScale;
3046
3047 glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment;
3048 glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment;
3049
3050 characterSpacingAdjustment = 0;
3051 wasLookupApplied = true;
3052 break;
3053 }
3054 }
3055 }
3056
3057 // If no Mark-to-Mark lookups were applied, check for potential Mark-to-Base lookup.
3058 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && !wasLookupApplied)
3059 {
3060 // Handle lookup for Mark-to-Base
3061 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph;
3062 uint baseGlyphIndex = baseGlyph.index;
3063 uint markGlyphIndex = m_cached_TextElement.glyphIndex;
3064 uint key = markGlyphIndex << 16 | baseGlyphIndex;
3065
3066 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord))
3067 {
3068 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale;
3069
3070 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment;
3071 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment;
3072
3073 characterSpacingAdjustment = 0;
3074 }
3075 }
3076 }
3077 }
3078
3079 // Adjust relevant text metrics
3080 elementAscentLine += glyphAdjustments.yPlacement;
3081 elementDescentLine += glyphAdjustments.yPlacement;
3082 #endregion
3083
3084
3085 // Initial Implementation for RTL support.
3086 #region Handle Right-to-Left
3087 if (m_isRightToLeft)
3088 {
3089 m_xAdvance -= currentGlyphMetrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * currentElementScale;
3090
3091 if (isWhiteSpace || charCode == 0x200B)
3092 m_xAdvance -= m_wordSpacing * currentEmScale;
3093 }
3094 #endregion
3095
3096
3097 // Handle Mono Spacing
3098 #region Handle Mono Spacing
3099 float monoAdvance = 0;
3100 if (m_monoSpacing != 0)
3101 {
3102 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ','))
3103 monoAdvance = (m_monoSpacing / 4 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
3104 else
3105 monoAdvance = (m_monoSpacing / 2 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
3106
3107 m_xAdvance += monoAdvance;
3108 }
3109 #endregion
3110
3111
3112 // Set Padding based on selected font style
3113 #region Handle Style Padding
3114 float boldSpacingAdjustment;
3115 float style_padding;
3116 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
3117 {
3118 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
3119 {
3120 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
3121 style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
3122
3123 // Clamp overall padding to Gradient Scale size.
3124 if (style_padding + padding > gradientScale)
3125 padding = gradientScale - style_padding;
3126 }
3127 else
3128 style_padding = 0;
3129
3130 boldSpacingAdjustment = m_currentFontAsset.boldSpacing;
3131 }
3132 else
3133 {
3134 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale) && m_currentMaterial.HasProperty(ShaderUtilities.ID_ScaleRatio_A))
3135 {
3136 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
3137 style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
3138
3139 // Clamp overall padding to Gradient Scale size.
3140 if (style_padding + padding > gradientScale)
3141 padding = gradientScale - style_padding;
3142 }
3143 else
3144 style_padding = 0;
3145
3146 boldSpacingAdjustment = 0;
3147 }
3148 #endregion Handle Style Padding
3149
3150
3151 // Determine the position of the vertices of the Character or Sprite.
3152 #region Calculate Vertices Position
3153 k_CalculateVerticesPositionMarker.Begin();
3154 Vector3 top_left;
3155 top_left.x = m_xAdvance + ((currentGlyphMetrics.horizontalBearingX * m_FXScale.x - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta));
3156 top_left.y = baselineOffset + (currentGlyphMetrics.horizontalBearingY + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset;
3157 top_left.z = 0;
3158
3159 Vector3 bottom_left;
3160 bottom_left.x = top_left.x;
3161 bottom_left.y = top_left.y - ((currentGlyphMetrics.height + padding * 2) * currentElementScale);
3162 bottom_left.z = 0;
3163
3164 Vector3 top_right;
3165 top_right.x = bottom_left.x + ((currentGlyphMetrics.width * m_FXScale.x + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta));
3166 top_right.y = top_left.y;
3167 top_right.z = 0;
3168
3169 Vector3 bottom_right;
3170 bottom_right.x = top_right.x;
3171 bottom_right.y = bottom_left.y;
3172 bottom_right.z = 0;
3173
3174 k_CalculateVerticesPositionMarker.End();
3175 #endregion
3176
3177
3178 // Check if we need to Shear the rectangles for Italic styles
3179 #region Handle Italic & Shearing
3180 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic))
3181 {
3182 // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount.
3183 float shear_value = m_ItalicAngle * 0.01f;
3184 float midPoint = ((m_currentFontAsset.m_FaceInfo.capLine - (m_currentFontAsset.m_FaceInfo.baseline + m_baselineOffset)) / 2) * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
3185 Vector3 topShear = new Vector3(shear_value * ((currentGlyphMetrics.horizontalBearingY + padding + style_padding - midPoint) * currentElementScale), 0, 0);
3186 Vector3 bottomShear = new Vector3(shear_value * (((currentGlyphMetrics.horizontalBearingY - currentGlyphMetrics.height - padding - style_padding - midPoint)) * currentElementScale), 0, 0);
3187
3188 top_left += topShear;
3189 bottom_left += bottomShear;
3190 top_right += topShear;
3191 bottom_right += bottomShear;
3192 }
3193 #endregion Handle Italics & Shearing
3194
3195
3196 // Handle Character FX Rotation
3197 #region Handle Character FX Rotation
3198 if (m_FXRotation != Quaternion.identity)
3199 {
3200 Matrix4x4 rotationMatrix = Matrix4x4.Rotate(m_FXRotation);
3201 Vector3 positionOffset = (top_right + bottom_left) / 2;
3202
3203 top_left = rotationMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset;
3204 bottom_left = rotationMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset;
3205 top_right = rotationMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset;
3206 bottom_right = rotationMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset;
3207 }
3208 #endregion
3209
3210
3211 // Store vertex information for the character or sprite.
3212 m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left;
3213 m_textInfo.characterInfo[m_characterCount].topLeft = top_left;
3214 m_textInfo.characterInfo[m_characterCount].topRight = top_right;
3215 m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right;
3216
3217 m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale;
3218 m_textInfo.characterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale;
3219 m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y);
3220
3221
3222 // Compute text metrics
3223 #region Compute Ascender & Descender values
3224 k_ComputeTextMetricsMarker.Begin();
3225 // Element Ascender in line space
3226 float elementAscender = m_textElementType == TMP_TextElementType.Character
3227 ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset
3228 : elementAscentLine * currentElementScale + m_baselineOffset;
3229
3230 // Element Descender in line space
3231 float elementDescender = m_textElementType == TMP_TextElementType.Character
3232 ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset
3233 : elementDescentLine * currentElementScale + m_baselineOffset;
3234
3235 float adjustedAscender = elementAscender;
3236 float adjustedDescender = elementDescender;
3237
3238 // Max line ascender and descender in line space
3239 bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine;
3240 if (isFirstCharacterOfLine || isWhiteSpace == false)
3241 {
3242 // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender
3243 if (m_baselineOffset != 0)
3244 {
3245 adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender);
3246 adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender);
3247 }
3248
3249 m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender);
3250 m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender);
3251 }
3252
3253 // Element Ascender and Descender in object space
3254 if (isFirstCharacterOfLine || isWhiteSpace == false)
3255 {
3256 m_textInfo.characterInfo[m_characterCount].adjustedAscender = adjustedAscender;
3257 m_textInfo.characterInfo[m_characterCount].adjustedDescender = adjustedDescender;
3258
3259 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
3260 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
3261 }
3262 else
3263 {
3264 m_textInfo.characterInfo[m_characterCount].adjustedAscender = m_maxLineAscender;
3265 m_textInfo.characterInfo[m_characterCount].adjustedDescender = m_maxLineDescender;
3266
3267 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset;
3268 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset;
3269 }
3270
3271 // Max text object ascender and cap height
3272 if (m_lineNumber == 0 || m_isNewPage)
3273 {
3274 if (isFirstCharacterOfLine || isWhiteSpace == false)
3275 {
3276 m_maxTextAscender = m_maxLineAscender;
3277 m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier);
3278 }
3279 }
3280
3281 // Page ascender
3282 if (m_lineOffset == 0)
3283 {
3284 if (isFirstCharacterOfLine || isWhiteSpace == false)
3285 m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender;
3286 }
3287 k_ComputeTextMetricsMarker.End();
3288 #endregion
3289
3290
3291 // Set Characters to not visible by default.
3292 m_textInfo.characterInfo[m_characterCount].isVisible = false;
3293
3294 bool isJustifiedOrFlush = (m_lineJustification & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush || (m_lineJustification & HorizontalAlignmentOptions.Justified) == HorizontalAlignmentOptions.Justified;
3295
3296 // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
3297 #region Handle Visible Characters
3298 if (charCode == 9 || ((m_TextWrappingMode == TextWrappingModes.PreserveWhitespace || m_TextWrappingMode == TextWrappingModes.PreserveWhitespaceNoWrap) && (isWhiteSpace || charCode == 0x200B)) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite)
3299 {
3300 k_HandleVisibleCharacterMarker.Begin();
3301
3302 m_textInfo.characterInfo[m_characterCount].isVisible = true;
3303
3304 #region Experimental Margin Shaper
3305 //Vector2 shapedMargins;
3306 //if (marginShaper)
3307 //{
3308 // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine);
3309 // if (shapedMargins.x < margins.x)
3310 // {
3311 // shapedMargins.x = m_marginLeft;
3312 // }
3313 // else
3314 // {
3315 // shapedMargins.x += m_marginLeft - margins.x;
3316 // }
3317 // if (shapedMargins.y < margins.z)
3318 // {
3319 // shapedMargins.y = m_marginRight;
3320 // }
3321 // else
3322 // {
3323 // shapedMargins.y += m_marginRight - margins.z;
3324 // }
3325 //}
3326 //else
3327 //{
3328 // shapedMargins.x = m_marginLeft;
3329 // shapedMargins.y = m_marginRight;
3330 //}
3331 //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y;
3332 //if (m_width != -1 && m_width < width)
3333 //{
3334 // width = m_width;
3335 //}
3336 //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x;
3337 #endregion
3338
3339 float marginLeft = m_marginLeft;
3340 float marginRight = m_marginRight;
3341
3342 // Injected characters do not override margins
3343 if (isInjectedCharacter)
3344 {
3345 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft;
3346 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight;
3347 }
3348
3349 widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight;
3350
3351 // Calculate the line breaking width of the text.
3352 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale);
3353 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
3354
3355 int testedCharacterCount = m_characterCount;
3356
3357 // Handling of current line Vertical Bounds
3358 #region Current Line Vertical Bounds Check
3359 if (textHeight > marginHeight + 0.0001f)
3360 {
3361 k_HandleVerticalLineBreakingMarker.Begin();
3362
3363 // Set isTextOverflowing and firstOverflowCharacterIndex
3364 if (m_firstOverflowCharacterIndex == -1)
3365 m_firstOverflowCharacterIndex = m_characterCount;
3366
3367 // Check if Auto-Size is enabled
3368 if (m_enableAutoSizing)
3369 {
3370 // Handle Line spacing adjustments
3371 #region Line Spacing Adjustments
3372 if (m_lineSpacingDelta > m_lineSpacingMax && m_lineOffset > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3373 {
3374 float adjustmentDelta = (marginHeight - textHeight) / m_lineNumber;
3375
3376 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax);
3377
3378 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "].");
3379 k_HandleVerticalLineBreakingMarker.End();
3380 k_HandleVisibleCharacterMarker.End();
3381 k_GenerateTextPhaseIMarker.End();
3382 k_GenerateTextMarker.End();
3383 return;
3384 }
3385 #endregion
3386
3387
3388 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3389 #region Text Auto-Sizing (Text greater than vertical bounds)
3390 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3391 {
3392 m_maxFontSize = m_fontSize;
3393
3394 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3395 m_fontSize -= sizeDelta;
3396 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3397
3398 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3399 k_HandleVerticalLineBreakingMarker.End();
3400 k_HandleVisibleCharacterMarker.End();
3401 k_GenerateTextPhaseIMarker.End();
3402 k_GenerateTextMarker.End();
3403 return;
3404 }
3405 #endregion Text Auto-Sizing
3406 }
3407
3408 // Handle Vertical Overflow on current line
3409 switch (m_overflowMode)
3410 {
3411 case TextOverflowModes.Overflow:
3412 case TextOverflowModes.ScrollRect:
3413 case TextOverflowModes.Masking:
3414 // Nothing happens as vertical bounds are ignored in this mode.
3415 break;
3416
3417 case TextOverflowModes.Truncate:
3418 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3419
3420 characterToSubstitute.index = testedCharacterCount;
3421 characterToSubstitute.unicode = 0x03;
3422 k_HandleVerticalLineBreakingMarker.End();
3423 k_HandleVisibleCharacterMarker.End();
3424 continue;
3425
3426 case TextOverflowModes.Ellipsis:
3427 if (m_EllipsisInsertionCandidateStack.Count == 0)
3428 {
3429 i = -1;
3430 m_characterCount = 0;
3431 characterToSubstitute.index = 0;
3432 characterToSubstitute.unicode = 0x03;
3433 m_firstCharacterOfLine = 0;
3434 k_HandleVerticalLineBreakingMarker.End();
3435 k_HandleVisibleCharacterMarker.End();
3436 continue;
3437 }
3438
3439 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3440 i = RestoreWordWrappingState(ref ellipsisState);
3441
3442 i -= 1;
3443 m_characterCount -= 1;
3444 characterToSubstitute.index = m_characterCount;
3445 characterToSubstitute.unicode = 0x2026;
3446
3447 restoreCount += 1;
3448 k_HandleVerticalLineBreakingMarker.End();
3449 k_HandleVisibleCharacterMarker.End();
3450 continue;
3451
3452 case TextOverflowModes.Linked:
3453 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3454
3455 if (m_linkedTextComponent != null)
3456 {
3457 m_linkedTextComponent.text = text;
3458 m_linkedTextComponent.m_inputSource = m_inputSource;
3459 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3460 m_linkedTextComponent.ForceMeshUpdate();
3461
3462 m_isTextTruncated = true;
3463 }
3464
3465 // Truncate remaining text
3466 characterToSubstitute.index = testedCharacterCount;
3467 characterToSubstitute.unicode = 0x03;
3468 k_HandleVerticalLineBreakingMarker.End();
3469 k_HandleVisibleCharacterMarker.End();
3470 continue;
3471
3472 case TextOverflowModes.Page:
3473 // End layout of text if first character / page doesn't fit.
3474 if (i < 0 || testedCharacterCount == 0)
3475 {
3476 i = -1;
3477 m_characterCount = 0;
3478 characterToSubstitute.index = 0;
3479 characterToSubstitute.unicode = 0x03;
3480 k_HandleVerticalLineBreakingMarker.End();
3481 k_HandleVisibleCharacterMarker.End();
3482 continue;
3483 }
3484 else if (m_maxLineAscender - m_maxLineDescender > marginHeight + 0.0001f)
3485 {
3486 // Current line exceeds the height of the text container
3487 // as such we stop on the previous line.
3488 i = RestoreWordWrappingState(ref m_SavedLineState);
3489
3490 characterToSubstitute.index = testedCharacterCount;
3491 characterToSubstitute.unicode = 0x03;
3492 k_HandleVerticalLineBreakingMarker.End();
3493 k_HandleVisibleCharacterMarker.End();
3494 continue;
3495 }
3496
3497 // Go back to previous line and re-layout
3498 i = RestoreWordWrappingState(ref m_SavedLineState);
3499
3500 m_isNewPage = true;
3501 m_firstCharacterOfLine = m_characterCount;
3502 m_maxLineAscender = k_LargeNegativeFloat;
3503 m_maxLineDescender = k_LargePositiveFloat;
3504 m_startOfLineAscender = 0;
3505
3506 m_xAdvance = 0 + tag_Indent;
3507 m_lineOffset = 0;
3508 m_maxTextAscender = 0;
3509 m_PageAscender = 0;
3510 m_lineNumber += 1;
3511 m_pageNumber += 1;
3512
3513 // Should consider saving page data here
3514 k_HandleVerticalLineBreakingMarker.End();
3515 k_HandleVisibleCharacterMarker.End();
3516 continue;
3517 }
3518
3519 k_HandleVerticalLineBreakingMarker.End();
3520 }
3521 #endregion
3522
3523
3524 // Handling of Horizontal Bounds
3525 #region Current Line Horizontal Bounds Check
3526 if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f))
3527 {
3528 k_HandleHorizontalLineBreakingMarker.Begin();
3529
3530 // Handle Line Breaking (if still possible)
3531 if (m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine)
3532 {
3533 // Restore state to previous safe line breaking
3534 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3535
3536 // Compute potential new line offset in the event a line break is needed.
3537 float lineOffsetDelta = 0;
3538 if (m_lineHeight == TMP_Math.FLOAT_UNSET)
3539 {
3540 float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender;
3541 lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale;
3542 }
3543 else
3544 {
3545 lineOffsetDelta = m_lineHeight + m_lineSpacing * currentEmScale;
3546 m_IsDrivenLineSpacing = true;
3547 }
3548
3549 // Calculate new text height
3550 float newTextHeight = m_maxTextAscender + lineOffsetDelta + m_lineOffset - m_textInfo.characterInfo[m_characterCount].adjustedDescender;
3551
3552 // Replace Soft Hyphen by Hyphen Minus 0x2D
3553 #region Handle Soft Hyphenation
3554 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD && isSoftHyphenIgnored == false)
3555 {
3556 // Only inject Hyphen Minus if new line is possible
3557 if (m_overflowMode == TextOverflowModes.Overflow || newTextHeight < marginHeight + 0.0001f)
3558 {
3559 characterToSubstitute.index = m_characterCount - 1;
3560 characterToSubstitute.unicode = 0x2D;
3561
3562 i -= 1;
3563 m_characterCount -= 1;
3564 k_HandleHorizontalLineBreakingMarker.End();
3565 k_HandleVisibleCharacterMarker.End();
3566 continue;
3567 }
3568 }
3569
3570 isSoftHyphenIgnored = false;
3571
3572 // Ignore Soft Hyphen to prevent it from wrapping
3573 if (m_textInfo.characterInfo[m_characterCount].character == 0xAD)
3574 {
3575 isSoftHyphenIgnored = true;
3576 k_HandleHorizontalLineBreakingMarker.End();
3577 k_HandleVisibleCharacterMarker.End();
3578 continue;
3579 }
3580 #endregion
3581
3582 // Adjust character spacing before breaking up word if auto size is enabled
3583 if (m_enableAutoSizing && isFirstWordOfLine)
3584 {
3585 // Handle Character Width Adjustments
3586 #region Character Width Adjustments
3587 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3588 {
3589 float adjustedTextWidth = textWidth;
3590
3591 // Determine full width of the text
3592 if (m_charWidthAdjDelta > 0)
3593 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3594
3595 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3596 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3597 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3598
3599 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3600 k_HandleHorizontalLineBreakingMarker.End();
3601 k_HandleVisibleCharacterMarker.End();
3602 k_GenerateTextPhaseIMarker.End();
3603 k_GenerateTextMarker.End();
3604 return;
3605 }
3606 #endregion
3607
3608 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3609 #region Text Auto-Sizing (Text greater than vertical bounds)
3610 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3611 {
3612 m_maxFontSize = m_fontSize;
3613
3614 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3615 m_fontSize -= sizeDelta;
3616 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3617
3618 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3619 k_HandleHorizontalLineBreakingMarker.End();
3620 k_HandleVisibleCharacterMarker.End();
3621 k_GenerateTextPhaseIMarker.End();
3622 k_GenerateTextMarker.End();
3623 return;
3624 }
3625 #endregion Text Auto-Sizing
3626 }
3627
3628
3629 // Special handling if first word of line and non breaking space
3630 int savedSoftLineBreakingSpace = m_SavedSoftLineBreakState.previous_WordBreak;
3631 if (isFirstWordOfLine && savedSoftLineBreakingSpace != -1)
3632 {
3633 if (savedSoftLineBreakingSpace != lastSoftLineBreak)
3634 {
3635 i = RestoreWordWrappingState(ref m_SavedSoftLineBreakState);
3636 lastSoftLineBreak = savedSoftLineBreakingSpace;
3637
3638 // check if soft hyphen
3639 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD)
3640 {
3641 characterToSubstitute.index = m_characterCount - 1;
3642 characterToSubstitute.unicode = 0x2D;
3643
3644 i -= 1;
3645 m_characterCount -= 1;
3646 k_HandleHorizontalLineBreakingMarker.End();
3647 k_HandleVisibleCharacterMarker.End();
3648 continue;
3649 }
3650 }
3651 }
3652
3653 // Determine if new line of text would exceed the vertical bounds of text container
3654 if (newTextHeight > marginHeight + 0.0001f)
3655 {
3656 k_HandleVerticalLineBreakingMarker.Begin();
3657
3658 // Set isTextOverflowing and firstOverflowCharacterIndex
3659 if (m_firstOverflowCharacterIndex == -1)
3660 m_firstOverflowCharacterIndex = m_characterCount;
3661
3662 // Check if Auto-Size is enabled
3663 if (m_enableAutoSizing)
3664 {
3665 // Handle Line spacing adjustments
3666 #region Line Spacing Adjustments
3667 if (m_lineSpacingDelta > m_lineSpacingMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3668 {
3669 float adjustmentDelta = (marginHeight - newTextHeight) / (m_lineNumber + 1);
3670
3671 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax);
3672
3673 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "].");
3674 k_HandleVerticalLineBreakingMarker.End();
3675 k_HandleHorizontalLineBreakingMarker.End();
3676 k_HandleVisibleCharacterMarker.End();
3677 k_GenerateTextPhaseIMarker.End();
3678 k_GenerateTextMarker.End();
3679 return;
3680 }
3681 #endregion
3682
3683 // Handle Character Width Adjustments
3684 #region Character Width Adjustments
3685 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3686 {
3687 float adjustedTextWidth = textWidth;
3688
3689 // Determine full width of the text
3690 if (m_charWidthAdjDelta > 0)
3691 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3692
3693 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3694 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3695 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3696
3697 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3698 k_HandleVerticalLineBreakingMarker.End();
3699 k_HandleHorizontalLineBreakingMarker.End();
3700 k_HandleVisibleCharacterMarker.End();
3701 k_GenerateTextPhaseIMarker.End();
3702 k_GenerateTextMarker.End();
3703 return;
3704 }
3705 #endregion
3706
3707 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3708 #region Text Auto-Sizing (Text greater than vertical bounds)
3709 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3710 {
3711 m_maxFontSize = m_fontSize;
3712
3713 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3714 m_fontSize -= sizeDelta;
3715 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3716
3717 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3718 k_HandleVerticalLineBreakingMarker.End();
3719 k_HandleHorizontalLineBreakingMarker.End();
3720 k_HandleVisibleCharacterMarker.End();
3721 k_GenerateTextPhaseIMarker.End();
3722 k_GenerateTextMarker.End();
3723 return;
3724 }
3725 #endregion Text Auto-Sizing
3726 }
3727
3728 // Check Text Overflow Modes
3729 switch (m_overflowMode)
3730 {
3731 case TextOverflowModes.Overflow:
3732 case TextOverflowModes.ScrollRect:
3733 case TextOverflowModes.Masking:
3734 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3735 isStartOfNewLine = true;
3736 isFirstWordOfLine = true;
3737 k_HandleVerticalLineBreakingMarker.End();
3738 k_HandleHorizontalLineBreakingMarker.End();
3739 k_HandleVisibleCharacterMarker.End();
3740 continue;
3741
3742 case TextOverflowModes.Truncate:
3743 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3744
3745 characterToSubstitute.index = testedCharacterCount;
3746 characterToSubstitute.unicode = 0x03;
3747 k_HandleVerticalLineBreakingMarker.End();
3748 k_HandleHorizontalLineBreakingMarker.End();
3749 k_HandleVisibleCharacterMarker.End();
3750 continue;
3751
3752 case TextOverflowModes.Ellipsis:
3753 if (m_EllipsisInsertionCandidateStack.Count == 0)
3754 {
3755 i = -1;
3756 m_characterCount = 0;
3757 characterToSubstitute.index = 0;
3758 characterToSubstitute.unicode = 0x03;
3759 m_firstCharacterOfLine = 0;
3760 k_HandleVerticalLineBreakingMarker.End();
3761 k_HandleHorizontalLineBreakingMarker.End();
3762 k_HandleVisibleCharacterMarker.End();
3763 continue;
3764 }
3765
3766 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3767 i = RestoreWordWrappingState(ref ellipsisState);
3768
3769 i -= 1;
3770 m_characterCount -= 1;
3771 characterToSubstitute.index = m_characterCount;
3772 characterToSubstitute.unicode = 0x2026;
3773
3774 restoreCount += 1;
3775 k_HandleVerticalLineBreakingMarker.End();
3776 k_HandleHorizontalLineBreakingMarker.End();
3777 k_HandleVisibleCharacterMarker.End();
3778 continue;
3779
3780 case TextOverflowModes.Linked:
3781 if (m_linkedTextComponent != null)
3782 {
3783 m_linkedTextComponent.text = text;
3784 m_linkedTextComponent.m_inputSource = m_inputSource;
3785 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3786 m_linkedTextComponent.ForceMeshUpdate();
3787
3788 m_isTextTruncated = true;
3789 }
3790
3791 // Truncate remaining text
3792 characterToSubstitute.index = m_characterCount;
3793 characterToSubstitute.unicode = 0x03;
3794 k_HandleVerticalLineBreakingMarker.End();
3795 k_HandleHorizontalLineBreakingMarker.End();
3796 k_HandleVisibleCharacterMarker.End();
3797 continue;
3798
3799 case TextOverflowModes.Page:
3800 // Add new page
3801 m_isNewPage = true;
3802
3803 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3804
3805 m_startOfLineAscender = 0;
3806 m_lineOffset = 0;
3807 m_maxTextAscender = 0;
3808 m_PageAscender = 0;
3809 m_pageNumber += 1;
3810
3811 isStartOfNewLine = true;
3812 isFirstWordOfLine = true;
3813 k_HandleVerticalLineBreakingMarker.End();
3814 k_HandleHorizontalLineBreakingMarker.End();
3815 k_HandleVisibleCharacterMarker.End();
3816 continue;
3817 }
3818 }
3819 else
3820 {
3821 //if (m_enableAutoSizing && isFirstWordOfLine)
3822 //{
3823 // // Handle Character Width Adjustments
3824 // #region Character Width Adjustments
3825 // if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3826 // {
3827 // //m_AutoSizeIterationCount = 0;
3828 // float adjustedTextWidth = textWidth;
3829
3830 // // Determine full width of the text
3831 // if (m_charWidthAdjDelta > 0)
3832 // adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3833
3834 // float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3835 // m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3836 // m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3837
3838 // //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3839
3840 // GenerateTextMesh();
3841 // return;
3842 // }
3843 // #endregion
3844 //}
3845
3846 // New line of text does not exceed vertical bounds of text container
3847 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3848 isStartOfNewLine = true;
3849 isFirstWordOfLine = true;
3850 k_HandleHorizontalLineBreakingMarker.End();
3851 k_HandleVisibleCharacterMarker.End();
3852 continue;
3853 }
3854 }
3855 else
3856 {
3857 if (m_enableAutoSizing && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3858 {
3859 // Handle Character Width Adjustments
3860 #region Character Width Adjustments
3861 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
3862 {
3863 float adjustedTextWidth = textWidth;
3864
3865 // Determine full width of the text
3866 if (m_charWidthAdjDelta > 0)
3867 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3868
3869 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3870 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3871 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3872
3873 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3874 k_HandleHorizontalLineBreakingMarker.End();
3875 k_HandleVisibleCharacterMarker.End();
3876 k_GenerateTextPhaseIMarker.End();
3877 k_GenerateTextMarker.End();
3878 return;
3879 }
3880 #endregion
3881
3882 // Handle Text Auto-sizing resulting from text exceeding horizontal bounds.
3883 #region Text Exceeds Horizontal Bounds - Reducing Point Size
3884 if (m_fontSize > m_fontSizeMin)
3885 {
3886 // Reset character width adjustment delta
3887 //m_charWidthAdjDelta = 0;
3888
3889 // Adjust Point Size
3890 m_maxFontSize = m_fontSize;
3891
3892 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3893 m_fontSize -= sizeDelta;
3894 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3895
3896 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3897 k_HandleHorizontalLineBreakingMarker.End();
3898 k_HandleVisibleCharacterMarker.End();
3899 k_GenerateTextPhaseIMarker.End();
3900 k_GenerateTextMarker.End();
3901 return;
3902 }
3903 #endregion
3904
3905 }
3906
3907 // Check Text Overflow Modes
3908 switch (m_overflowMode)
3909 {
3910 case TextOverflowModes.Overflow:
3911 case TextOverflowModes.ScrollRect:
3912 case TextOverflowModes.Masking:
3913 // Nothing happens as horizontal bounds are ignored in this mode.
3914 break;
3915
3916 case TextOverflowModes.Truncate:
3917 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3918
3919 characterToSubstitute.index = testedCharacterCount;
3920 characterToSubstitute.unicode = 0x03;
3921 k_HandleHorizontalLineBreakingMarker.End();
3922 k_HandleVisibleCharacterMarker.End();
3923 continue;
3924
3925 case TextOverflowModes.Ellipsis:
3926 if (m_EllipsisInsertionCandidateStack.Count == 0)
3927 {
3928 i = -1;
3929 m_characterCount = 0;
3930 characterToSubstitute.index = 0;
3931 characterToSubstitute.unicode = 0x03;
3932 m_firstCharacterOfLine = 0;
3933 k_HandleHorizontalLineBreakingMarker.End();
3934 k_HandleVisibleCharacterMarker.End();
3935 continue;
3936 }
3937
3938 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3939 i = RestoreWordWrappingState(ref ellipsisState);
3940
3941 i -= 1;
3942 m_characterCount -= 1;
3943 characterToSubstitute.index = m_characterCount;
3944 characterToSubstitute.unicode = 0x2026;
3945
3946 restoreCount += 1;
3947 k_HandleHorizontalLineBreakingMarker.End();
3948 k_HandleVisibleCharacterMarker.End();
3949 continue;
3950
3951 case TextOverflowModes.Linked:
3952 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3953
3954 if (m_linkedTextComponent != null)
3955 {
3956 m_linkedTextComponent.text = text;
3957 m_linkedTextComponent.m_inputSource = m_inputSource;
3958 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3959 m_linkedTextComponent.ForceMeshUpdate();
3960
3961 m_isTextTruncated = true;
3962 }
3963
3964 // Truncate text the overflows the vertical bounds
3965 characterToSubstitute.index = m_characterCount;
3966 characterToSubstitute.unicode = 0x03;
3967 k_HandleHorizontalLineBreakingMarker.End();
3968 k_HandleVisibleCharacterMarker.End();
3969 continue;
3970 }
3971
3972 }
3973
3974 k_HandleHorizontalLineBreakingMarker.End();
3975 }
3976 #endregion
3977
3978
3979 // Special handling of characters that are not ignored at the end of a line.
3980 if (isWhiteSpace)
3981 {
3982 m_textInfo.characterInfo[m_characterCount].isVisible = false;
3983 m_lastVisibleCharacterOfLine = m_characterCount;
3984 m_lineVisibleSpaceCount = m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
3985 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft;
3986 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight;
3987 m_textInfo.spaceCount += 1;
3988
3989 if (charCode == 0xA0)
3990 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
3991 }
3992 else if (charCode == 0xAD)
3993 {
3994 m_textInfo.characterInfo[m_characterCount].isVisible = false;
3995 }
3996 else
3997 {
3998 // Determine Vertex Color
3999 Color32 vertexColor;
4000 if (m_overrideHtmlColors)
4001 vertexColor = m_fontColor32;
4002 else
4003 vertexColor = m_htmlColor;
4004
4005 k_SaveGlyphVertexDataMarker.Begin();
4006 // Store Character & Sprite Vertex Information
4007 if (m_textElementType == TMP_TextElementType.Character)
4008 {
4009 // Save Character Vertex Data
4010 SaveGlyphVertexInfo(padding, style_padding, vertexColor);
4011 }
4012 else if (m_textElementType == TMP_TextElementType.Sprite)
4013 {
4014 SaveSpriteVertexInfo(vertexColor);
4015 }
4016 k_SaveGlyphVertexDataMarker.End();
4017
4018 if (isStartOfNewLine)
4019 {
4020 isStartOfNewLine = false;
4021 m_firstVisibleCharacterOfLine = m_characterCount;
4022 }
4023
4024 m_lineVisibleCharacterCount += 1;
4025 m_lastVisibleCharacterOfLine = m_characterCount;
4026 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft;
4027 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight;
4028 }
4029
4030 k_HandleVisibleCharacterMarker.End();
4031 }
4032 else
4033 {
4034 k_HandleWhiteSpacesMarker.Begin();
4035
4036 // Special handling for text overflow linked mode
4037 #region Check Vertical Bounds
4038 if (m_overflowMode == TextOverflowModes.Linked && (charCode == 10 || charCode == 11))
4039 {
4040 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
4041
4042 int testedCharacterCount = m_characterCount;
4043
4044 if (textHeight > marginHeight + 0.0001f)
4045 {
4046 // Set isTextOverflowing and firstOverflowCharacterIndex
4047 if (m_firstOverflowCharacterIndex == -1)
4048 m_firstOverflowCharacterIndex = m_characterCount;
4049
4050 i = RestoreWordWrappingState(ref m_SavedLastValidState);
4051
4052 if (m_linkedTextComponent != null)
4053 {
4054 m_linkedTextComponent.text = text;
4055 m_linkedTextComponent.m_inputSource = m_inputSource;
4056 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
4057 m_linkedTextComponent.ForceMeshUpdate();
4058
4059 m_isTextTruncated = true;
4060 }
4061
4062 // Truncate remaining text
4063 characterToSubstitute.index = testedCharacterCount;
4064 characterToSubstitute.unicode = 0x03;
4065 k_HandleWhiteSpacesMarker.End();
4066 continue;
4067 }
4068 }
4069 #endregion
4070
4071 // Track # of spaces per line which is used for line justification.
4072 if ((charCode == 10 || charCode == 11 || charCode == 0xA0 || charCode == 0x2007 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060)
4073 {
4074 m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
4075 m_textInfo.spaceCount += 1;
4076 }
4077
4078 // Special handling for control characters like <NBSP>
4079 if (charCode == 0xA0)
4080 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
4081
4082 k_HandleWhiteSpacesMarker.End();
4083 }
4084 #endregion Handle Visible Characters
4085
4086
4087 // Tracking of potential insertion positions for Ellipsis character
4088 #region Track Potential Insertion Location for Ellipsis
4089 if (m_overflowMode == TextOverflowModes.Ellipsis && (isInjectedCharacter == false || charCode == 0x2D))
4090 {
4091 float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
4092 float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale;
4093 float marginLeft = m_marginLeft;
4094 float marginRight = m_marginRight;
4095
4096 // Use the scale and margins of the previous character if Line Feed (LF) is not the first character of a line.
4097 if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine)
4098 {
4099 fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
4100 scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale;
4101 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft;
4102 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight;
4103 }
4104
4105 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
4106 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale;
4107 float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight;
4108
4109 if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f)
4110 {
4111 SaveWordWrappingState(ref m_SavedEllipsisState, i, m_characterCount);
4112 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState);
4113 }
4114 }
4115 #endregion
4116
4117
4118 // Store Rectangle positions for each Character.
4119 #region Store Character Data
4120 m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber;
4121 m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber;
4122
4123 if (charCode != 10 && charCode != 11 && charCode != 13 && isInjectedCharacter == false /* && charCode != 8230 */ || m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
4124 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
4125 #endregion Store Character Data
4126
4127
4128 // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
4129 #region XAdvance, Tabulation & Stops
4130 k_ComputeCharacterAdvanceMarker.Begin();
4131 if (charCode == 9)
4132 {
4133 float tabSize = m_currentFontAsset.m_FaceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale;
4134 // Adjust horizontal tab depending on RTL
4135 if (m_isRightToLeft)
4136 {
4137 float tabs = Mathf.Floor(m_xAdvance / tabSize) * tabSize;
4138 m_xAdvance = tabs < m_xAdvance ? tabs : m_xAdvance - tabSize;
4139 }
4140 else
4141 {
4142 float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
4143 m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
4144 }
4145 }
4146 else if (m_monoSpacing != 0)
4147 {
4148 float monoAdjustment;
4149 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ','))
4150 monoAdjustment = m_monoSpacing / 2 - monoAdvance;
4151 else
4152 monoAdjustment = m_monoSpacing - monoAdvance;
4153
4154 m_xAdvance += (monoAdjustment + ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment) * currentEmScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
4155
4156 if (isWhiteSpace || charCode == 0x200B)
4157 m_xAdvance += m_wordSpacing * currentEmScale;
4158 }
4159 else if (m_isRightToLeft)
4160 {
4161 m_xAdvance -= ((glyphAdjustments.xAdvance * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta));
4162
4163 if (isWhiteSpace || charCode == 0x200B)
4164 m_xAdvance -= m_wordSpacing * currentEmScale;
4165 }
4166 else
4167 {
4168 m_xAdvance += ((currentGlyphMetrics.horizontalAdvance * m_FXScale.x + glyphAdjustments.xAdvance) * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
4169
4170 if (isWhiteSpace || charCode == 0x200B)
4171 m_xAdvance += m_wordSpacing * currentEmScale;
4172 }
4173
4174 // Store xAdvance information
4175 m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance;
4176 k_ComputeCharacterAdvanceMarker.End();
4177 #endregion Tabulation & Stops
4178
4179
4180 // Handle Carriage Return
4181 #region Carriage Return
4182 if (charCode == 13)
4183 {
4184 k_HandleCarriageReturnMarker.Begin();
4185 m_xAdvance = 0 + tag_Indent;
4186 k_HandleCarriageReturnMarker.End();
4187 }
4188 #endregion Carriage Return
4189
4190
4191 // Tracking of text overflow page mode
4192 #region Save PageInfo
4193 k_SavePageInfoMarker.Begin();
4194 if (m_overflowMode == TextOverflowModes.Page && charCode != 10 && charCode != 11 && charCode != 13 && charCode != 0x2028 && charCode != 0x2029)
4195 {
4196 // Check if we need to increase allocations for the pageInfo array.
4197 if (m_pageNumber + 1 > m_textInfo.pageInfo.Length)
4198 TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true);
4199
4200 m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender;
4201 m_textInfo.pageInfo[m_pageNumber].descender = m_ElementDescender < m_textInfo.pageInfo[m_pageNumber].descender
4202 ? m_ElementDescender
4203 : m_textInfo.pageInfo[m_pageNumber].descender;
4204
4205 if (m_isNewPage)
4206 {
4207 m_isNewPage = false;
4208 m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
4209 }
4210
4211 // Last index
4212 m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount;
4213 }
4214 k_SavePageInfoMarker.End();
4215 #endregion Save PageInfo
4216
4217
4218 // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
4219 #region Check for Line Feed and Last Character
4220 if (charCode == 10 || charCode == 11 || charCode == 0x03 || charCode == 0x2028 || charCode == 0x2029 || (charCode == 0x2D && isInjectedCharacter) || m_characterCount == totalCharacterCount - 1)
4221 {
4222 k_HandleLineTerminationMarker.Begin();
4223
4224 // Adjust current line spacing (if necessary) before inserting new line
4225 float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender;
4226 if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage)
4227 {
4228 //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber);
4229 AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta);
4230 m_ElementDescender -= baselineAdjustmentDelta;
4231 m_lineOffset += baselineAdjustmentDelta;
4232
4233 // Adjust saved ellipsis state only if we are adjusting the same line number
4234 if (m_SavedEllipsisState.lineNumber == m_lineNumber)
4235 {
4236 m_SavedEllipsisState = m_EllipsisInsertionCandidateStack.Pop();
4237 m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta;
4238 m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta;
4239 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState);
4240 }
4241 }
4242 m_isNewPage = false;
4243
4244 // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
4245 float lineAscender = m_maxLineAscender - m_lineOffset;
4246 float lineDescender = m_maxLineDescender - m_lineOffset;
4247
4248 // Update maxDescender and maxVisibleDescender
4249 m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender;
4250 if (!isMaxVisibleDescenderSet)
4251 maxVisibleDescender = m_ElementDescender;
4252
4253 if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
4254 isMaxVisibleDescenderSet = true;
4255
4256 // Save Line Information
4257 m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
4258 m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
4259 m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount;
4260 m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
4261
4262 m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
4263 m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
4264 m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1) - m_lineVisibleCharacterCount;
4265 m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
4266 m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
4267 m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale);
4268 m_textInfo.lineInfo[m_lineNumber].width = widthOfTextArea;
4269
4270 if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
4271 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
4272
4273 float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
4274 if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible)
4275 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset);
4276 else
4277 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset);
4278
4279 m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
4280 m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
4281 m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
4282 m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
4283
4284 // Add new line if not last line or character.
4285 if (charCode == 10 || charCode == 11 || (charCode == 0x2D && isInjectedCharacter) || charCode == 0x2028 || charCode == 0x2029)
4286 {
4287 // Store the state of the line before starting on the new line.
4288 SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount);
4289
4290 m_lineNumber += 1;
4291 isStartOfNewLine = true;
4292 ignoreNonBreakingSpace = false;
4293 isFirstWordOfLine = true;
4294
4295 m_firstCharacterOfLine = m_characterCount + 1;
4296 m_lineVisibleCharacterCount = 0;
4297 m_lineVisibleSpaceCount = 0;
4298
4299 // Check to make sure Array is large enough to hold a new line.
4300 if (m_lineNumber >= m_textInfo.lineInfo.Length)
4301 ResizeLineExtents(m_lineNumber);
4302
4303 float lastVisibleAscender = m_textInfo.characterInfo[m_characterCount].adjustedAscender;
4304
4305 // Apply Line Spacing with special handling for VT char(11)
4306 if (m_lineHeight == TMP_Math.FLOAT_UNSET)
4307 {
4308 float lineOffsetDelta = 0 - m_maxLineDescender + lastVisibleAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale;
4309 m_lineOffset += lineOffsetDelta;
4310 m_IsDrivenLineSpacing = false;
4311 }
4312 else
4313 {
4314 m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale;
4315 m_IsDrivenLineSpacing = true;
4316 }
4317
4318 m_maxLineAscender = k_LargeNegativeFloat;
4319 m_maxLineDescender = k_LargePositiveFloat;
4320 m_startOfLineAscender = lastVisibleAscender;
4321
4322 m_xAdvance = 0 + tag_LineIndent + tag_Indent;
4323
4324 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
4325 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount);
4326
4327 m_characterCount += 1;
4328
4329 k_HandleLineTerminationMarker.End();
4330
4331 continue;
4332 }
4333
4334 // If End of Text
4335 if (charCode == 0x03)
4336 i = m_TextProcessingArray.Length;
4337
4338 k_HandleLineTerminationMarker.End();
4339 }
4340 #endregion Check for Linefeed or Last Character
4341
4342
4343 // Track extents of the text
4344 #region Track Text Extents
4345 k_SaveTextExtentMarker.Begin();
4346 // Determine the bounds of the Mesh.
4347 if (m_textInfo.characterInfo[m_characterCount].isVisible)
4348 {
4349 m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x);
4350 m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y);
4351
4352 m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x);
4353 m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y);
4354
4355 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y));
4356 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y));
4357 }
4358 k_SaveTextExtentMarker.End();
4359 #endregion Track Text Extents
4360
4361
4362 // Save State of Mesh Creation for handling of Word Wrapping
4363 #region Save Word Wrapping State
4364 if ((m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked)
4365 {
4366 k_SaveProcessingStatesMarker.Begin();
4367
4368 bool shouldSaveHardLineBreak = false;
4369 bool shouldSaveSoftLineBreak = false;
4370
4371 if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
4372 {
4373 // Ignore Hyphen (0x2D) when preceded by a whitespace
4374 if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false)
4375 {
4376 isFirstWordOfLine = false;
4377 shouldSaveHardLineBreak = true;
4378
4379 // Reset soft line breaking point since we now have a valid hard break point.
4380 m_SavedSoftLineBreakState.previous_WordBreak = -1;
4381 }
4382 }
4383 // Handling for East Asian scripts
4384 else if (m_isNonBreakingSpace == false && (TMP_TextParsingUtilities.IsHangul(charCode) && TMP_Settings.useModernHangulLineBreakingRules == false || TMP_TextParsingUtilities.IsCJK(charCode)))
4385 {
4386 bool isCurrentLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.Contains(charCode);
4387 bool isNextFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.Contains(m_textInfo.characterInfo[m_characterCount + 1].character);
4388
4389 if (isCurrentLeadingCharacter == false)
4390 {
4391 if (isNextFollowingCharacter == false)
4392 {
4393 isFirstWordOfLine = false;
4394 shouldSaveHardLineBreak = true;
4395 }
4396
4397 if (isFirstWordOfLine)
4398 {
4399 // Special handling for non-breaking space and soft line breaks
4400 if (isWhiteSpace)
4401 shouldSaveSoftLineBreak = true;
4402
4403 shouldSaveHardLineBreak = true;
4404 }
4405 }
4406 else
4407 {
4408 if (isFirstWordOfLine && isFirstCharacterOfLine)
4409 {
4410 // Special handling for non-breaking space and soft line breaks
4411 if (isWhiteSpace)
4412 shouldSaveSoftLineBreak = true;
4413
4414 shouldSaveHardLineBreak = true;
4415 }
4416 }
4417 }
4418 // Special handling for Latin characters followed by a CJK character.
4419 else if (m_isNonBreakingSpace == false && m_characterCount + 1 < totalCharacterCount && TMP_TextParsingUtilities.IsCJK(m_textInfo.characterInfo[m_characterCount + 1].character))
4420 {
4421 shouldSaveHardLineBreak = true;
4422 }
4423 else if (isFirstWordOfLine)
4424 {
4425 // Special handling for non-breaking space and soft line breaks
4426 if (isWhiteSpace && charCode != 0xA0 || (charCode == 0xAD && isSoftHyphenIgnored == false))
4427 shouldSaveSoftLineBreak = true;
4428
4429 shouldSaveHardLineBreak = true;
4430 }
4431
4432 // Save potential Hard lines break
4433 if (shouldSaveHardLineBreak)
4434 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
4435
4436 // Save potential Soft line break
4437 if (shouldSaveSoftLineBreak)
4438 SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount);
4439
4440 k_SaveProcessingStatesMarker.End();
4441 }
4442 #endregion Save Word Wrapping State
4443
4444 // Consider only saving state on base glyphs
4445 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount);
4446
4447 m_characterCount += 1;
4448 }
4449
4450 // Check Auto Sizing and increase font size to fill text container.
4451 #region Check Auto-Sizing (Upper Font Size Bounds)
4452 fontSizeDelta = m_maxFontSize - m_minFontSize;
4453 if (/* !m_isCharacterWrappingEnabled && */ m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
4454 {
4455 // Reset character width adjustment delta
4456 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
4457 m_charWidthAdjDelta = 0;
4458
4459 m_minFontSize = m_fontSize;
4460
4461 float sizeDelta = Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f);
4462 m_fontSize += sizeDelta;
4463 m_fontSize = Mathf.Min((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMax);
4464
4465 //Debug.Log("[" + m_AutoSizeIterationCount + "] Increasing Point Size from [" + m_minFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
4466 k_GenerateTextPhaseIMarker.End();
4467 k_GenerateTextMarker.End();
4468 return;
4469 }
4470 #endregion End Auto-sizing Check
4471
4472 m_IsAutoSizePointSizeSet = true;
4473
4474 if (m_AutoSizeIterationCount >= m_AutoSizeMaxIterationCount)
4475 Debug.Log("Auto Size Iteration Count: " + m_AutoSizeIterationCount + ". Final Point Size: " + m_fontSize);
4476
4477 // If there are no visible characters or only character is End of Text (0x03)... no need to continue
4478 if (m_characterCount == 0 || (m_characterCount == 1 && charCode == 0x03))
4479 {
4480 ClearMesh();
4481
4482 // Event indicating the text has been regenerated.
4483 TMPro_EventManager.ON_TEXT_CHANGED(this);
4484 k_GenerateTextPhaseIMarker.End();
4485 k_GenerateTextMarker.End();
4486 return;
4487 }
4488
4489 // End Sampling of Phase I
4490 k_GenerateTextPhaseIMarker.End();
4491
4492 // *** PHASE II of Text Generation ***
4493 k_GenerateTextPhaseIIMarker.Begin();
4494 int last_vert_index = m_materialReferences[m_Underline.materialIndex].referenceCount * 4;
4495
4496 // Partial clear of the vertices array to mark unused vertices as degenerate.
4497 m_textInfo.meshInfo[0].Clear(false);
4498
4499 // Handle Text Alignment
4500 #region Text Vertical Alignment
4501 Vector3 anchorOffset = Vector3.zero;
4502 Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners();
4503
4504 // Handle Vertical Text Alignment
4505 switch (m_VerticalAlignment)
4506 {
4507 // Top Vertically
4508 case VerticalAlignmentOptions.Top:
4509 if (m_overflowMode != TextOverflowModes.Page)
4510 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxTextAscender - margins.y, 0);
4511 else
4512 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0);
4513 break;
4514
4515 // Middle Vertically
4516 case VerticalAlignmentOptions.Middle:
4517 if (m_overflowMode != TextOverflowModes.Page)
4518 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxTextAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0);
4519 else
4520 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_textInfo.pageInfo[pageToDisplay].ascender + margins.y + m_textInfo.pageInfo[pageToDisplay].descender - margins.w) / 2, 0);
4521 break;
4522
4523 // Bottom Vertically
4524 case VerticalAlignmentOptions.Bottom:
4525 if (m_overflowMode != TextOverflowModes.Page)
4526 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0);
4527 else
4528 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0);
4529 break;
4530
4531 // Baseline Vertically
4532 case VerticalAlignmentOptions.Baseline:
4533 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0);
4534 break;
4535
4536 // Midline Vertically
4537 case VerticalAlignmentOptions.Geometry:
4538 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0);
4539 break;
4540
4541 // Capline Vertically
4542 case VerticalAlignmentOptions.Capline:
4543 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0);
4544 break;
4545 }
4546 #endregion
4547
4548 // Initialization for Second Pass
4549 Vector3 justificationOffset = Vector3.zero;
4550 Vector3 offset = Vector3.zero;
4551 // int vert_index_X4 = 0;
4552 // int sprite_index_X4 = 0;
4553
4554 int wordCount = 0;
4555 int lineCount = 0;
4556 int lastLine = 0;
4557 bool isFirstSeperator = false;
4558
4559 bool isStartOfWord = false;
4560 int wordFirstChar = 0;
4561 int wordLastChar = 0;
4562
4563 // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more.
4564 // Variables used to handle Canvas Render Modes and SDF Scaling
4565 bool isCameraAssigned = m_canvas.worldCamera == null ? false : true;
4566 float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y;
4567 RenderMode canvasRenderMode = m_canvas.renderMode;
4568 float canvasScaleFactor = m_canvas.scaleFactor;
4569
4570 Color32 underlineColor = Color.white;
4571 Color32 strikethroughColor = Color.white;
4572 HighlightState highlightState = new HighlightState(new Color32(255, 255, 0, 64), TMP_Offset.zero);
4573 float xScale = 0;
4574 float xScaleMax = 0;
4575 float underlineStartScale = 0;
4576 float underlineEndScale = 0;
4577 float underlineMaxScale = 0;
4578 float underlineBaseLine = k_LargePositiveFloat;
4579 int lastPage = 0;
4580
4581 float strikethroughPointSize = 0;
4582 float strikethroughScale = 0;
4583 float strikethroughBaseline = 0;
4584
4585 TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo;
4586 #region Handle Line Justification & UV Mapping & Character Visibility & More
4587 for (int i = 0; i < m_characterCount; i++)
4588 {
4589 TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset;
4590
4591 char unicode = characterInfos[i].character;
4592 bool isWhiteSpace = char.IsWhiteSpace(unicode);
4593
4594 int currentLine = characterInfos[i].lineNumber;
4595 TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine];
4596 lineCount = currentLine + 1;
4597
4598 HorizontalAlignmentOptions lineAlignment = lineInfo.alignment;
4599
4600 // Process Line Justification
4601 #region Handle Line Justification
4602 switch (lineAlignment)
4603 {
4604 case HorizontalAlignmentOptions.Left:
4605 if (!m_isRightToLeft)
4606 justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0);
4607 else
4608 justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0);
4609 break;
4610
4611 case HorizontalAlignmentOptions.Center:
4612 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
4613 break;
4614
4615 case HorizontalAlignmentOptions.Geometry:
4616 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0);
4617 break;
4618
4619 case HorizontalAlignmentOptions.Right:
4620 if (!m_isRightToLeft)
4621 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0);
4622 else
4623 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
4624 break;
4625
4626 case HorizontalAlignmentOptions.Justified:
4627 case HorizontalAlignmentOptions.Flush:
4628 // Skip Zero Width Characters and spaces outside of the margins.
4629 if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break;
4630
4631 char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character;
4632
4633 bool isFlush = (lineAlignment & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush;
4634
4635 // In Justified mode, all lines are justified except the last one.
4636 // In Flush mode, all lines are justified.
4637 if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width)
4638 {
4639 // First character of each line.
4640 if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter)
4641 {
4642 if (!m_isRightToLeft)
4643 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0);
4644 else
4645 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
4646
4647 if (char.IsSeparator(unicode))
4648 isFirstSeperator = true;
4649 else
4650 isFirstSeperator = false;
4651 }
4652 else
4653 {
4654 float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance;
4655 int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount;
4656 int spaces = lineInfo.spaceCount - lineInfo.controlCharacterCount;
4657
4658 if (isFirstSeperator) { spaces -= 1; visibleCount += 1; }
4659
4660 float ratio = spaces > 0 ? m_wordWrappingRatios : 1;
4661
4662 if (spaces < 1) spaces = 1;
4663
4664 if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator(unicode)))
4665 {
4666 if (!m_isRightToLeft)
4667 justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0);
4668 else
4669 justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0);
4670 }
4671 else
4672 {
4673 if (!m_isRightToLeft)
4674 justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0);
4675 else
4676 justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0);
4677 }
4678 }
4679 }
4680 else
4681 {
4682 if (!m_isRightToLeft)
4683 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified.
4684 else
4685 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified.
4686 }
4687 //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount);
4688 break;
4689 }
4690 #endregion End Text Justification
4691
4692 offset = anchorOffset + justificationOffset;
4693
4694 // Handle UV2 mapping options and packing of scale information into UV2.
4695 #region Handling of UV2 mapping & Scale packing
4696 bool isCharacterVisible = characterInfos[i].isVisible;
4697 if (isCharacterVisible)
4698 {
4699 TMP_TextElementType elementType = characterInfos[i].elementType;
4700 switch (elementType)
4701 {
4702 // CHARACTERS
4703 case TMP_TextElementType.Character:
4704 Extents lineExtents = lineInfo.lineExtents;
4705 float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x;
4706
4707 // Setup UV2 based on Character Mapping Options Selected
4708 #region Handle UV Mapping Options
4709 switch (m_horizontalMapping)
4710 {
4711 case TextureMappingOptions.Character:
4712 characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x;
4713 characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x;
4714 characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x;
4715 characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x;
4716 break;
4717
4718 case TextureMappingOptions.Line:
4719 if (m_textAlignment != TextAlignmentOptions.Justified)
4720 {
4721 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4722 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4723 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4724 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4725 break;
4726 }
4727 else // Special Case if Justified is used in Line Mode.
4728 {
4729 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4730 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4731 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4732 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4733 break;
4734 }
4735
4736 case TextureMappingOptions.Paragraph:
4737 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4738 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4739 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4740 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4741 break;
4742
4743 case TextureMappingOptions.MatchAspect:
4744
4745 switch (m_verticalMapping)
4746 {
4747 case TextureMappingOptions.Character:
4748 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
4749 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
4750 characterInfos[i].vertex_TR.uv2.y = 0; // + m_uvOffset.y;
4751 characterInfos[i].vertex_BR.uv2.y = 1; // + m_uvOffset.y;
4752 break;
4753
4754 case TextureMappingOptions.Line:
4755 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
4756 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
4757 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4758 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4759 break;
4760
4761 case TextureMappingOptions.Paragraph:
4762 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
4763 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
4764 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4765 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4766 break;
4767
4768 case TextureMappingOptions.MatchAspect:
4769 Debug.Log("ERROR: Cannot Match both Vertical & Horizontal.");
4770 break;
4771 }
4772
4773 //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned
4774 float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
4775
4776 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
4777 characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x;
4778 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
4779 characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x;
4780 break;
4781 }
4782
4783 switch (m_verticalMapping)
4784 {
4785 case TextureMappingOptions.Character:
4786 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
4787 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
4788 characterInfos[i].vertex_TR.uv2.y = 1; // + m_uvOffset.y;
4789 characterInfos[i].vertex_BR.uv2.y = 0; // + m_uvOffset.y;
4790 break;
4791
4792 case TextureMappingOptions.Line:
4793 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
4794 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
4795 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4796 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4797 break;
4798
4799 case TextureMappingOptions.Paragraph:
4800 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
4801 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
4802 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4803 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4804 break;
4805
4806 case TextureMappingOptions.MatchAspect:
4807 float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
4808
4809 characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
4810 characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
4811 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4812 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4813 break;
4814 }
4815 #endregion
4816
4817 // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio.
4818 #region Pack Scale into UV2
4819 xScale = characterInfos[i].scale * (1 - m_charWidthAdjDelta);
4820 if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1;
4821
4822 switch (canvasRenderMode)
4823 {
4824 case RenderMode.ScreenSpaceOverlay:
4825 xScale *= Mathf.Abs(lossyScale) / canvasScaleFactor;
4826 break;
4827 case RenderMode.ScreenSpaceCamera:
4828 xScale *= isCameraAssigned ? Mathf.Abs(lossyScale) : 1;
4829 break;
4830 case RenderMode.WorldSpace:
4831 xScale *= Mathf.Abs(lossyScale);
4832 break;
4833 }
4834
4835 // Set SDF Scale
4836 characterInfos[i].vertex_BL.uv.w = xScale;
4837 characterInfos[i].vertex_TL.uv.w = xScale;
4838 characterInfos[i].vertex_TR.uv.w = xScale;
4839 characterInfos[i].vertex_BR.uv.w = xScale;
4840 #endregion
4841 break;
4842
4843 // SPRITES
4844 case TMP_TextElementType.Sprite:
4845 // Nothing right now
4846 break;
4847 }
4848
4849 // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode.
4850 #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode
4851 if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page)
4852 {
4853 characterInfos[i].vertex_BL.position += offset;
4854 characterInfos[i].vertex_TL.position += offset;
4855 characterInfos[i].vertex_TR.position += offset;
4856 characterInfos[i].vertex_BR.position += offset;
4857 }
4858 else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay)
4859 {
4860 characterInfos[i].vertex_BL.position += offset;
4861 characterInfos[i].vertex_TL.position += offset;
4862 characterInfos[i].vertex_TR.position += offset;
4863 characterInfos[i].vertex_BR.position += offset;
4864 }
4865 else
4866 {
4867 characterInfos[i].vertex_BL.position = Vector3.zero;
4868 characterInfos[i].vertex_TL.position = Vector3.zero;
4869 characterInfos[i].vertex_TR.position = Vector3.zero;
4870 characterInfos[i].vertex_BR.position = Vector3.zero;
4871 characterInfos[i].isVisible = false;
4872 }
4873 #endregion
4874
4875
4876 // Fill Vertex Buffers for the various types of element
4877 if (elementType == TMP_TextElementType.Character)
4878 {
4879 FillCharacterVertexBuffers(i);
4880 }
4881 else if (elementType == TMP_TextElementType.Sprite)
4882 {
4883 FillSpriteVertexBuffers(i);
4884 }
4885 }
4886 #endregion
4887
4888 // Apply Alignment and Justification Offset
4889 m_textInfo.characterInfo[i].bottomLeft += offset;
4890 m_textInfo.characterInfo[i].topLeft += offset;
4891 m_textInfo.characterInfo[i].topRight += offset;
4892 m_textInfo.characterInfo[i].bottomRight += offset;
4893
4894 m_textInfo.characterInfo[i].origin += offset.x;
4895 m_textInfo.characterInfo[i].xAdvance += offset.x;
4896
4897 m_textInfo.characterInfo[i].ascender += offset.y;
4898 m_textInfo.characterInfo[i].descender += offset.y;
4899 m_textInfo.characterInfo[i].baseLine += offset.y;
4900
4901 // Update MeshExtents
4902 if (isCharacterVisible)
4903 {
4904 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[i].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[i].bottomLeft.y));
4905 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[i].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[i].topLeft.y));
4906 }
4907
4908 // Need to recompute lineExtent to account for the offset from justification.
4909 #region Adjust lineExtents resulting from alignment offset
4910 if (currentLine != lastLine || i == m_characterCount - 1)
4911 {
4912 // Update the previous line's extents
4913 if (currentLine != lastLine)
4914 {
4915 m_textInfo.lineInfo[lastLine].baseline += offset.y;
4916 m_textInfo.lineInfo[lastLine].ascender += offset.y;
4917 m_textInfo.lineInfo[lastLine].descender += offset.y;
4918
4919 m_textInfo.lineInfo[lastLine].maxAdvance += offset.x;
4920
4921 m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender);
4922 m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender);
4923 }
4924
4925 // Update the current line's extents
4926 if (i == m_characterCount - 1)
4927 {
4928 m_textInfo.lineInfo[currentLine].baseline += offset.y;
4929 m_textInfo.lineInfo[currentLine].ascender += offset.y;
4930 m_textInfo.lineInfo[currentLine].descender += offset.y;
4931
4932 m_textInfo.lineInfo[currentLine].maxAdvance += offset.x;
4933
4934 m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender);
4935 m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender);
4936 }
4937 }
4938 #endregion
4939
4940
4941 // Track Word Count per line and for the object
4942 #region Track Word Count
4943 if (char.IsLetterOrDigit(unicode) || unicode == 0x2D || unicode == 0xAD || unicode == 0x2010 || unicode == 0x2011)
4944 {
4945 if (isStartOfWord == false)
4946 {
4947 isStartOfWord = true;
4948 wordFirstChar = i;
4949 }
4950
4951 // If last character is a word
4952 if (isStartOfWord && i == m_characterCount - 1)
4953 {
4954 int size = m_textInfo.wordInfo.Length;
4955 int index = m_textInfo.wordCount;
4956
4957 if (m_textInfo.wordCount + 1 > size)
4958 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
4959
4960 wordLastChar = i;
4961
4962 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
4963 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
4964 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
4965 m_textInfo.wordInfo[index].textComponent = this;
4966
4967 wordCount += 1;
4968 m_textInfo.wordCount += 1;
4969 m_textInfo.lineInfo[currentLine].wordCount += 1;
4970 }
4971 }
4972 else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == 0x200B || i == m_characterCount - 1))
4973 {
4974 if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (unicode == 39 || unicode == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character))
4975 {
4976
4977 }
4978 else
4979 {
4980 wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(unicode) ? i : i - 1;
4981 isStartOfWord = false;
4982
4983 int size = m_textInfo.wordInfo.Length;
4984 int index = m_textInfo.wordCount;
4985
4986 if (m_textInfo.wordCount + 1 > size)
4987 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
4988
4989 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
4990 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
4991 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
4992 m_textInfo.wordInfo[index].textComponent = this;
4993
4994 wordCount += 1;
4995 m_textInfo.wordCount += 1;
4996 m_textInfo.lineInfo[currentLine].wordCount += 1;
4997 }
4998 }
4999 #endregion
5000
5001
5002 // Setup & Handle Underline
5003 #region Underline
5004 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
5005 bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline;
5006 if (isUnderline)
5007 {
5008 bool isUnderlineVisible = true;
5009 int currentPage = m_textInfo.characterInfo[i].pageNumber;
5010 m_textInfo.characterInfo[i].underlineVertexIndex = last_vert_index;
5011
5012 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
5013 isUnderlineVisible = false;
5014
5015 // We only use the scale of visible characters.
5016 if (!isWhiteSpace && unicode != 0x200B)
5017 {
5018 underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale);
5019 xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale));
5020 underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.m_FaceInfo.underlineOffset * underlineMaxScale);
5021 lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages.
5022 }
5023
5024 if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
5025 {
5026 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
5027 { }
5028 else
5029 {
5030 beginUnderline = true;
5031 underlineStartScale = m_textInfo.characterInfo[i].scale;
5032 if (underlineMaxScale == 0)
5033 {
5034 underlineMaxScale = underlineStartScale;
5035 xScaleMax = xScale;
5036 }
5037 underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0);
5038 underlineColor = m_textInfo.characterInfo[i].underlineColor;
5039 }
5040 }
5041
5042 // End Underline if text only contains one character.
5043 if (beginUnderline && m_characterCount == 1)
5044 {
5045 beginUnderline = false;
5046 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
5047 underlineEndScale = m_textInfo.characterInfo[i].scale;
5048
5049 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
5050 underlineMaxScale = 0;
5051 xScaleMax = 0;
5052 underlineBaseLine = k_LargePositiveFloat;
5053 }
5054 else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
5055 {
5056 // Terminate underline at previous visible character if space or carriage return.
5057 if (isWhiteSpace || unicode == 0x200B)
5058 {
5059 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
5060 underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0);
5061 underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale;
5062 }
5063 else
5064 { // End underline if last character of the line.
5065 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
5066 underlineEndScale = m_textInfo.characterInfo[i].scale;
5067 }
5068
5069 beginUnderline = false;
5070 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
5071 underlineMaxScale = 0;
5072 xScaleMax = 0;
5073 underlineBaseLine = k_LargePositiveFloat;
5074 }
5075 else if (beginUnderline && !isUnderlineVisible)
5076 {
5077 beginUnderline = false;
5078 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
5079 underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
5080
5081 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
5082 underlineMaxScale = 0;
5083 xScaleMax = 0;
5084 underlineBaseLine = k_LargePositiveFloat;
5085 }
5086 else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor))
5087 {
5088 // End underline if underline color has changed.
5089 beginUnderline = false;
5090 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
5091 underlineEndScale = m_textInfo.characterInfo[i].scale;
5092
5093 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
5094 underlineMaxScale = 0;
5095 xScaleMax = 0;
5096 underlineBaseLine = k_LargePositiveFloat;
5097 }
5098 }
5099 else
5100 {
5101 // End Underline
5102 if (beginUnderline == true)
5103 {
5104 beginUnderline = false;
5105 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
5106 underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
5107
5108 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
5109 underlineMaxScale = 0;
5110 xScaleMax = 0;
5111 underlineBaseLine = k_LargePositiveFloat;
5112 }
5113 }
5114 #endregion
5115
5116
5117 // Setup & Handle Strikethrough
5118 #region Strikethrough
5119 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
5120 bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough;
5121 float strikethroughOffset = currentFontAsset.m_FaceInfo.strikethroughOffset;
5122
5123 if (isStrikethrough)
5124 {
5125 bool isStrikeThroughVisible = true;
5126 m_textInfo.characterInfo[i].strikethroughVertexIndex = last_vert_index;
5127
5128 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay))
5129 isStrikeThroughVisible = false;
5130
5131 if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
5132 {
5133 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
5134 { }
5135 else
5136 {
5137 beginStrikethrough = true;
5138 strikethroughPointSize = m_textInfo.characterInfo[i].pointSize;
5139 strikethroughScale = m_textInfo.characterInfo[i].scale;
5140 strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
5141 strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor;
5142 strikethroughBaseline = m_textInfo.characterInfo[i].baseLine;
5143 //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start);
5144 }
5145 }
5146
5147 // End Strikethrough if text only contains one character.
5148 if (beginStrikethrough && m_characterCount == 1)
5149 {
5150 beginStrikethrough = false;
5151 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
5152
5153 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5154 }
5155 else if (beginStrikethrough && i == lineInfo.lastCharacterIndex)
5156 {
5157 // Terminate Strikethrough at previous visible character if space or carriage return.
5158 if (isWhiteSpace || unicode == 0x200B)
5159 {
5160 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
5161 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
5162 }
5163 else
5164 {
5165 // Terminate Strikethrough at last character of line.
5166 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
5167 }
5168
5169 beginStrikethrough = false;
5170 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5171 }
5172 else if (beginStrikethrough && i < m_characterCount && (m_textInfo.characterInfo[i + 1].pointSize != strikethroughPointSize || !TMP_Math.Approximately(m_textInfo.characterInfo[i + 1].baseLine + offset.y, strikethroughBaseline)))
5173 {
5174 // Terminate Strikethrough if scale changes.
5175 beginStrikethrough = false;
5176
5177 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
5178 if (i > lastVisibleCharacterIndex)
5179 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
5180 else
5181 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
5182
5183 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5184 //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3"));
5185 }
5186 else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID())
5187 {
5188 // Terminate Strikethrough if font asset changes.
5189 beginStrikethrough = false;
5190 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
5191
5192 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5193 }
5194 else if (beginStrikethrough && !isStrikeThroughVisible)
5195 {
5196 // Terminate Strikethrough if character is not visible.
5197 beginStrikethrough = false;
5198 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
5199
5200 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5201 }
5202 }
5203 else
5204 {
5205 // End Strikethrough
5206 if (beginStrikethrough == true)
5207 {
5208 beginStrikethrough = false;
5209 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
5210
5211 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
5212 }
5213 }
5214 #endregion
5215
5216
5217 // HANDLE TEXT HIGHLIGHTING
5218 #region Text Highlighting
5219 bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight;
5220 if (isHighlight)
5221 {
5222 bool isHighlightVisible = true;
5223 int currentPage = m_textInfo.characterInfo[i].pageNumber;
5224
5225 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
5226 isHighlightVisible = false;
5227
5228 if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
5229 {
5230 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
5231 { }
5232 else
5233 {
5234 beginHighlight = true;
5235 highlight_start = k_LargePositiveVector2;
5236 highlight_end = k_LargeNegativeVector2;
5237 highlightState = m_textInfo.characterInfo[i].highlightState;
5238 }
5239 }
5240
5241 if (beginHighlight)
5242 {
5243 TMP_CharacterInfo currentCharacter = m_textInfo.characterInfo[i];
5244 HighlightState currentState = currentCharacter.highlightState;
5245
5246 bool isColorTransition = false;
5247
5248 // Handle Highlight color changes
5249 if (highlightState != currentState)
5250 {
5251 // Adjust previous highlight section to prevent a gaps between sections.
5252 if (isWhiteSpace)
5253 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.origin) / 2;
5254 else
5255 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2;
5256
5257 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender);
5258 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender);
5259
5260 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
5261
5262 beginHighlight = true;
5263 highlight_start = new Vector2(highlight_end.x, currentCharacter.descender - currentState.padding.bottom);
5264
5265 if (isWhiteSpace)
5266 highlight_end = new Vector2(currentCharacter.xAdvance + currentState.padding.right, currentCharacter.ascender + currentState.padding.top);
5267 else
5268 highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top);
5269
5270 highlightState = currentState;
5271
5272 isColorTransition = true;
5273 }
5274
5275 if (!isColorTransition)
5276 {
5277 if (isWhiteSpace)
5278 {
5279 // Use the Min / Max of glyph metrics if white space.
5280 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.origin - highlightState.padding.left);
5281 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.xAdvance + highlightState.padding.right);
5282 }
5283 else
5284 {
5285 // Use the Min / Max of character bounds
5286 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left);
5287 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right);
5288 }
5289
5290 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom);
5291 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender + highlightState.padding.top);
5292 }
5293 }
5294
5295 // End Highlight if text only contains one character.
5296 if (beginHighlight && m_characterCount == 1)
5297 {
5298 beginHighlight = false;
5299
5300 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
5301 }
5302 else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
5303 {
5304 beginHighlight = false;
5305 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
5306 }
5307 else if (beginHighlight && !isHighlightVisible)
5308 {
5309 beginHighlight = false;
5310 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
5311 }
5312 }
5313 else
5314 {
5315 // End Highlight
5316 if (beginHighlight == true)
5317 {
5318 beginHighlight = false;
5319 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
5320 }
5321 }
5322 #endregion
5323
5324 lastLine = currentLine;
5325 }
5326 #endregion
5327
5328 // Set vertex count for Underline geometry
5329 m_textInfo.meshInfo[m_Underline.materialIndex].vertexCount = last_vert_index;
5330
5331 // METRICS ABOUT THE TEXT OBJECT
5332 m_textInfo.characterCount = m_characterCount;
5333 m_textInfo.spriteCount = m_spriteCount;
5334 m_textInfo.lineCount = lineCount;
5335 m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1;
5336 m_textInfo.pageCount = m_pageNumber + 1;
5337
5338 // End Sampling of Phase II
5339 k_GenerateTextPhaseIIMarker.End();
5340
5341 // Phase III - Update Mesh Vertex Data
5342 k_GenerateTextPhaseIIIMarker.Begin();
5343
5344 if (m_renderMode == TextRenderFlags.Render && IsActive())
5345 {
5346 // Event to allow users to modify the content of the text info before the text is rendered.
5347 OnPreRenderText?.Invoke(m_textInfo);
5348
5349 // Must ensure the Canvas support the additional vertex attributes used by TMP.
5350 // This could be optimized based on canvas render mode settings but gets complicated to handle with multiple text objects using different material presets.
5351 if (m_canvas.additionalShaderChannels != (AdditionalCanvasShaderChannels)25)
5352 m_canvas.additionalShaderChannels |= (AdditionalCanvasShaderChannels)25;
5353
5354 // Sort the geometry of the text object if needed.
5355 if (m_geometrySortingOrder != VertexSortingOrder.Normal)
5356 m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse);
5357
5358 // Upload Mesh Data
5359 m_mesh.MarkDynamic();
5360 m_mesh.vertices = m_textInfo.meshInfo[0].vertices;
5361 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0);
5362 m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
5363 //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4;
5364 m_mesh.colors32 = m_textInfo.meshInfo[0].colors32;
5365
5366 // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.RecalcualteBounds.
5367 m_mesh.RecalculateBounds();
5368 //m_mesh.bounds = new Bounds(new Vector3((m_meshExtents.max.x + m_meshExtents.min.x) / 2, (m_meshExtents.max.y + m_meshExtents.min.y) / 2, 0) + offset, new Vector3(m_meshExtents.max.x - m_meshExtents.min.x, m_meshExtents.max.y - m_meshExtents.min.y, 0));
5369
5370 m_canvasRenderer.SetMesh(m_mesh);
5371
5372 // Cache CanvasRenderer color of the parent text object.
5373 Color parentBaseColor = m_canvasRenderer.GetColor();
5374
5375 bool isCullTransparentMeshEnabled = m_canvasRenderer.cullTransparentMesh;
5376
5377 for (int i = 1; i < m_textInfo.materialCount; i++)
5378 {
5379 // Clear unused vertices
5380 m_textInfo.meshInfo[i].ClearUnusedVertices();
5381
5382 if (m_subTextObjects[i] == null) continue;
5383
5384 // Sort the geometry of the sub-text objects if needed.
5385 if (m_geometrySortingOrder != VertexSortingOrder.Normal)
5386 m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse);
5387
5388 //m_subTextObjects[i].mesh.MarkDynamic();
5389 m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices;
5390 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
5391 m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
5392 //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
5393 m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32;
5394
5395 m_subTextObjects[i].mesh.RecalculateBounds();
5396
5397 m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
5398
5399 // Set CanvasRenderer color to match the parent text object.
5400 m_subTextObjects[i].canvasRenderer.SetColor(parentBaseColor);
5401
5402 // Make sure Cull Transparent Mesh of the sub objects matches the parent
5403 m_subTextObjects[i].canvasRenderer.cullTransparentMesh = isCullTransparentMeshEnabled;
5404
5405 // Sync RaycastTarget property with parent text object
5406 m_subTextObjects[i].raycastTarget = this.raycastTarget;
5407 }
5408 }
5409
5410 // Update culling if it has to be delayed due to text layout being dirty.
5411 if (m_ShouldUpdateCulling)
5412 UpdateCulling();
5413
5414 // Event indicating the text has been regenerated.
5415 TMPro_EventManager.ON_TEXT_CHANGED(this);
5416
5417 //Debug.Log("***** Done rendering text object ID " + GetInstanceID() + ". *****");
5418
5419 // End Sampling
5420 k_GenerateTextPhaseIIIMarker.End();
5421 k_GenerateTextMarker.End();
5422 }
5423
5424
5425 /// <summary>
5426 /// Method to return the local corners of the Text Container or RectTransform.
5427 /// </summary>
5428 /// <returns></returns>
5429 protected override Vector3[] GetTextContainerLocalCorners()
5430 {
5431 if (m_rectTransform == null) m_rectTransform = this.rectTransform;
5432
5433 m_rectTransform.GetLocalCorners(m_RectTransformCorners);
5434
5435 return m_RectTransformCorners;
5436 }
5437
5438
5439 /// <summary>
5440 /// Method to Enable or Disable child SubMesh objects.
5441 /// </summary>
5442 /// <param name="state"></param>
5443 protected override void SetActiveSubMeshes(bool state)
5444 {
5445 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5446 {
5447 if (m_subTextObjects[i].enabled != state)
5448 m_subTextObjects[i].enabled = state;
5449 }
5450 }
5451
5452
5453 /// <summary>
5454 /// Destroy Sub Mesh Objects
5455 /// </summary>
5456 protected override void DestroySubMeshObjects()
5457 {
5458 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5459 DestroyImmediate(m_subTextObjects[i]);
5460 }
5461
5462
5463 /// <summary>
5464 /// Method returning the compound bounds of the text object and child sub objects.
5465 /// </summary>
5466 /// <returns></returns>
5467 protected override Bounds GetCompoundBounds()
5468 {
5469 Bounds mainBounds = m_mesh.bounds;
5470 Vector3 min = mainBounds.min;
5471 Vector3 max = mainBounds.max;
5472
5473 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5474 {
5475 Bounds subBounds = m_subTextObjects[i].mesh.bounds;
5476 min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x;
5477 min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y;
5478
5479 max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x;
5480 max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y;
5481 }
5482
5483 Vector3 center = (min + max) / 2;
5484 Vector2 size = max - min;
5485 return new Bounds(center, size);
5486 }
5487
5488 internal override Rect GetCanvasSpaceClippingRect()
5489 {
5490 if (m_canvas == null || m_canvas.rootCanvas == null || m_mesh == null)
5491 return Rect.zero;
5492
5493 Transform rootCanvasTransform = m_canvas.rootCanvas.transform;
5494 Bounds compoundBounds = GetCompoundBounds();
5495
5496 Vector2 position = rootCanvasTransform.InverseTransformPoint(m_rectTransform.position);
5497
5498 Vector2 canvasLossyScale = rootCanvasTransform.lossyScale;
5499 Vector2 lossyScale = m_rectTransform.lossyScale / canvasLossyScale;
5500
5501 return new Rect(position + compoundBounds.min * lossyScale, compoundBounds.size * lossyScale);
5502 }
5503
5504 /// <summary>
5505 /// Method to Update Scale in UV2
5506 /// </summary>
5507 //void UpdateSDFScale(float lossyScale)
5508 //{
5509 // // TODO: Resolve - Underline / Strikethrough segments not getting their SDF Scale adjusted.
5510
5511 // //Debug.Log("Updating SDF Scale.");
5512
5513 // // Return if we don't have a valid reference to a Canvas.
5514 // if (m_canvas == null)
5515 // {
5516 // m_canvas = GetCanvas();
5517 // if (m_canvas == null) return;
5518 // }
5519
5520 // lossyScale = lossyScale == 0 ? 1 : lossyScale;
5521
5522 // float xScale = 0;
5523 // float canvasScaleFactor = m_canvas.scaleFactor;
5524
5525 // if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay)
5526 // xScale = lossyScale / canvasScaleFactor;
5527 // else if (m_canvas.renderMode == RenderMode.ScreenSpaceCamera)
5528 // xScale = m_canvas.worldCamera != null ? lossyScale : 1;
5529 // else
5530 // xScale = lossyScale;
5531
5532 // // Iterate through each of the characters.
5533 // for (int i = 0; i < m_textInfo.characterCount; i++)
5534 // {
5535 // // Only update scale for visible characters.
5536 // if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character)
5537 // {
5538 // float scale = xScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta);
5539 // if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1;
5540
5541 // int index = m_textInfo.characterInfo[i].materialReferenceIndex;
5542 // int vertexIndex = m_textInfo.characterInfo[i].vertexIndex;
5543
5544 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale;
5545 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale;
5546 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale;
5547 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale;
5548 // }
5549 // }
5550
5551 // // Push the updated uv2 scale information to the meshes.
5552 // for (int i = 0; i < m_textInfo.materialCount; i++)
5553 // {
5554 // if (i == 0)
5555 // {
5556 // m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
5557 // m_canvasRenderer.SetMesh(m_mesh);
5558 // }
5559 // else
5560 // {
5561 // m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
5562 // m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
5563 // }
5564 // }
5565 //}
5566
5567 /// <summary>
5568 /// Method to update the SDF Scale in UV2.
5569 /// </summary>
5570 /// <param name="scaleDelta"></param>
5571 void UpdateSDFScale(float scaleDelta)
5572 {
5573 if (scaleDelta == 0 || scaleDelta == float.PositiveInfinity || scaleDelta == float.NegativeInfinity)
5574 {
5575 m_havePropertiesChanged = true;
5576 OnPreRenderCanvas();
5577 return;
5578 }
5579
5580 for (int materialIndex = 0; materialIndex < m_textInfo.materialCount; materialIndex ++)
5581 {
5582 TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex];
5583
5584 for (int i = 0; i < meshInfo.uvs0.Length; i++)
5585 {
5586 meshInfo.uvs0[i].w *= Mathf.Abs(scaleDelta);
5587 }
5588 }
5589
5590 // Push the updated uv0 scale information to the meshes.
5591 for (int i = 0; i < m_textInfo.materialCount; i++)
5592 {
5593 if (i == 0)
5594 {
5595 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0);
5596 m_canvasRenderer.SetMesh(m_mesh);
5597 }
5598 else
5599 {
5600 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
5601 m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
5602 }
5603 }
5604 }
5605 #endregion
5606 }
5607}