Is there any way to do something like GetTextSize but vertical?

Currently GetTextSize returns the size of the string without text wrapping

What would be the best way to get a size with a restriction on the X axis? I’ve tried just taking the X and dividing it by the max X size that I want but it often ends up leaving weird empty “lines” at the bottom of text labels.

3 Likes

What I often do is GetTextSize() with a ridiculous number on the Y axis as math.huge() doesn’t seem to work with this. For example.

game:GetService(“TextService”):GetTextSize(“this is a string”, 14, Enum.Font.SourceSans, Vector2.new(200, 5000))

Setting your X axis or the 200 in this case to how wide your TextLabel or such as your restriction should work.

1 Like

Max number you can use is 2147483583, if you want to be more precise instead of using 5000.

1 Like

If you don’t need to do the operation too frequently or on too big a text, then you can binary search to find where the line breaks are yourself. That is, something like:

  • GetTextSize()… is that bigger or smaller than the line width?

  • If bigger, then break the text exactly in half, and call GetTextSize() on the first half. Is it still bigger than the line width?

  • If the second time it was smaller, now GetTextSize() to break the second half of the line into parts until you find the break

  • Etc, repeat until you’ve broken up the line into pieces.

If you implement this with divide and conquer (half each time) then it should take few enough calls to GetTextSize that it won’t cost too much (Probably about 20 calls for a couple hundred characters of text). As a bonus if you do it that way you can even choose exactly when the text wraps rather than it being hard-coded on spaces.

2 Likes

That seems to be such a random number to set as the limit. Is there any actual reasoning behind it?

I don’t know any of the technical details or terms, but it’s the max signed 16 bit integer. Someone else could probably give more detail or correct me on this if they feel like it.

The max is apparently 2147483583, as corrected by Anaminus.

1 Like

Here. This literally creates a dummy ScreenGui and TextLabel in StarterGui, sets the defined parameters, retrieves the resulting TextBounds, and then deletes the created ScreenGui. This only works in the client. The interface is the same as GetTextSize().

local function GetTextSizeWithWrapping(str, fontSize, font, frameSize)
	local sg = Instance.new("ScreenGui", game:GetService("StarterGui"))
	sg.Archivable = false
	local label = Instance.new("TextLabel", sg)
	local bounds = nil
	
	local ok, message = pcall(function()
		label.Size = UDim2.new(0, frameSize.X, 0, frameSize.Y)
		label.TextWrapped = true
		label.TextSize = fontSize
		label.Text = str
		label.Font = font
		label.Archivable = false
		
		bounds = label.TextBounds
	end)
	
	sg:Destroy()
	
	if not ok then
		error(message)
	end
	
	return bounds
end

If you’re listening to Descendant/ChildAdded on StarterGui, then a side effect is that this will trigger those events. You probably aren’t, though.

edit @stravant TextWrapping doesn’t break words into different lines. Your method does.

1 Like

This is not true for GetTextSize. The upper limit in this is case appears to be 2147483583. This is 2^31-2^6-1 exactly, but 2^30 works for most intents and purposes. Regardless, this upper-bound issue should be reported as a bug.

This is also not the case. Here’s some code that demonstrates how GetTextSize is used, and that it returns a value that wraps accurately.

local MaxSize = 2^30
local MaxX = 200
local Font = Enum.Font.SourceSans
local FontSize = 14

-- Includes some random newlines
local text = [[
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum.]]

local screen = Instance.new("ScreenGui", game.StarterGui)
local label = Instance.new("TextLabel", screen)
label.Position = UDim2.new(0, 100, 0, 100)
label.TextWrapped = true
label.TextSize = FontSize
label.Font = Font
for i = 1,#text+1 do
	local v = game:GetService("TextService"):GetTextSize(text:sub(i), FontSize, Font, Vector2.new(MaxX, MaxSize))
	if v.x > MaxX then
		error(string.format("%d: size (%d) is greater than max (%d)!", i, v.x, MaxX))
	end
	label.Text = text:sub(i)
	label.Size = UDim2.new(0, v.x, 0, v.y)
	wait()
end
6 Likes

Huh… So it does.

Then I’ve clearly been doing something totally wrong because in my own experimenting I couldn’t get it to give me a height greater than one line, I’m so confused at what you did different

Maybe I’ve just been screwing up the X value somehow??