This is by design as layout components check the Preferred Width which in the case of text is the length of the longest line without breaking it. Note that Preferred Width is calculated with Word Wrapping disabled.
Text alignment is based on the text metrics and not the geometry of characters. The following information should help you get a better understanding of this.
A Character is assigned a Unicode code point and references a glyph which is the visual representation of this character. You can look at this relationship between characters and glyphs by looking at the Character and Glyph tables any font assets.
Glyphs contains a GlyphRect and Glyph Metrics.
The GlyphRect defines the position of the glyph in the atlas texture.
The GlyphMetrics defines the placement / positioning / layout of the glyph in a line of text. These metrics are defined by the designer of the font.
For instance, assuming we were placing the first glyph on a line of text. The drawing position would be at x = 0 and on the baseline which is y = 0. These metrics go back to the days of plotters and define the position / placement of the pen to draw the glyph. From this (0, 0) the pen would then be moved / offset by the X Bearing and Y Bearing moving the pen to the top left where it would begin to draw the glyph one line at a time using the width and height metrics.
Using the TMP_TextInfoDebugTool.cs which is included in the TMP Examples & Extras we can visualize these metrics including the Origin and Advance of a glyph as seen below.
In the above example using LiberationSans where the text is left aligned, you can see the origin of the “i” which is at x = 0. The pen is then offset by the X Bearing which is a positive value for this glyph and then also offset by the Y Bearing which is our top left corner and position of the top left vertex. From this position we add the width and we have to top right vertex. Subtract the height and we have our bottom left and right vertex.
We will repeat this process for the next glyph moving the pen to the advance of our first glyph which will now become the origin of our second glyph.
Looking at the letter “i” which has a positive X Bearing, we can see the “i” is inside the RectTransform. By contrast, the letter “j” on the second line has a negative X Bearing resulting in the bottom part of the glyph being outside the RectTransform. This looks weird but as you can see in the above image, these X Bearings were defined as such to have their Stem align vertically.
In the next image where the text is center aligned, you can see each line is centered between the Origin and Max Advance of each line.
So back to your “!” and “W”, the alignment of these glyphs is related to their X Bearing where it looks like the text bounds are incorrect as the x min = 0 instead of the Origin of the left most glyph as seen below.
The teal box represents the text bounds which are incorrect in the above image as they should be from Origin to Advance.
To correct this issue, make the following change in the TMP_Text.cs file in the GetTextBounds() function which should be done in the TMP package contained in the Global Package Cache.
Image showing the corrected text bounds