Text bounding box issues.

Godot Version

4.4

Question

I’m using the UI system of Godot 4.4, and it can’t even properly obtain the bounding box of text. I’ve tried Label.get_character_bounds and text_server.shaped_text_get_size, but neither of them works properly. get_character_bounds unexpectedly returns a width and height of 0, 0, and shaped_text_get_size always returns 0, 0.

func _get_bounds():
	var text = label.text
	var font = label.get_theme_default_font()  # 获取 Label 的字体
	var font_size = label.get_theme_default_font_size() # 获取 Label 的字体大小
	var text_server = TextServerManager.get_primary_interface()
	var font_rid = font.get_rid()
	var shaped_text = text_server.create_shaped_text(TextServer.Direction.DIRECTION_AUTO,TextServer.Orientation.ORIENTATION_HORIZONTAL)
	print("shaped_text_add_string:%s-%s-%s-%s"%[shaped_text,text,font,font_size])
	text_server.shaped_text_add_string(shaped_text, text, [font_rid], font_size)
	print("label.size.x:%s"%[label.size.x])
	text_server.shaped_text_fit_to_width(shaped_text, label.size.x)
	var bounds= text_server.shaped_text_get_size(shaped_text)
	text_server.free_rid(shaped_text)
	return bounds

It sounds like you don’t like gdscript.

Anyway I created a control node, centred it, added a child label with some text and added this script to it:

@onready var label = $Label

func _ready() -> void:
	# get bounding box of label node
	var bounding_box = label.get_rect()
	print("Bounding Box:", bounding_box)

It outputs:

Bounding Box:[P: (0, 0), S: (149, 36)]

Which all seems fine.

Then I thought you were asking about text_size and labels have boundaries, so I added this:


func _ready() -> void:
	# get bounding box of label node
	var bounding_box = label.get_rect()
	print("Bounding Box:", bounding_box)

	# get text size after rendering on screen
	await get_tree().process_frame  # Wait for initial render
	var font = $Label.get_theme_font("font")
	var text_size = font.get_string_size($Label.text)
	print("Text size:", text_size)

Which output this:

Bounding Box:[P: (0, 0), S: (149, 36)]
Text size:(92, 23)

Which all seemed fine too.

Then I thought that you were using text_server and shaped text (which admittedly I have never used). So I tried it. Now this was a fiddly affair to say the least, especially getting the RIDs and primary canvas stuff to work, but after a bit of reading in the docs and adding a font to the theme, I have this:

extends Control

@onready var label = $Label


var draw_text = false
var shaped_text
var text_server

func _ready() -> void:
	# get bounding box of label node
	var bounding_box = label.get_rect()
	print("Bounding Box:", bounding_box)

	# get text size after rendering on screen
	await get_tree().process_frame  # Wait for initial render
	var font = $Label.get_theme_font("font")
	var text_size = font.get_string_size($Label.text)
	print("Text size:", text_size)
	
	# Do it again using TextServerManager
	await get_tree().process_frame
	var new_font = label.get_theme_font("font")
	var new_font_size = label.get_theme_font_size("font_size")
	text_server = TextServerManager.get_primary_interface()
	var font_rids = new_font.get_rids()
	shaped_text = text_server.create_shaped_text()
	text_server.shaped_text_add_string(shaped_text, "Hello World!", [font_rids[0]], new_font_size)
	text_server.shaped_text_shape(shaped_text)
	var size_of_text = text_server.shaped_text_get_size(shaped_text)
	print("Shaped Text size:", size_of_text)
	draw_text = true
	queue_redraw()


func _draw():
	if draw_text:
		var canvas_item = self.get_canvas_item()
		var text_position = Vector2(200,200)
		var color = Color(1, 1, 1)
		text_server.shaped_text_draw(shaped_text, canvas_item, text_position, -1, -1, color)

Which outputs this:

Bounding Box:[P: (0, 0), S: (149, 36)]
Text size:(92, 23)
Shaped Text size:(149, 36)

So that all seems to be working fine too.

Try replacing your get_rid with get_rids and your reference to it as [font_rids[0]] perhaps. I must admit the text_server stuff was fiddly, I have no plans to start using it anytime soon, but it does work.

Thank you for your reply. I’m just starting to learn Godot and everything is going well. The UI is the only part that gives me a headache. Regarding this current issue, font.get_multiline_string_size might be the API I’m looking for, but it seems inaccurate. Moreover, such a complex API is not very friendly for custom components…

var font:Font = $Control/Label.get_theme_font("font")
	var text_size = font.get_multiline_string_size(
		$Control/Label.text,HORIZONTAL_ALIGNMENT_LEFT,
		$Control/Label.size.x,
		$Control/Label.get_theme_default_font_size(),
		$Control/Label.get_line_count())
	print($Control/Label.size)
	print($Control/Label.get_rect())
	print("Text size:", text_size)
	
	$Control/ColorRect.position=$Control/Label.position
	$Control/ColorRect.size=text_size
(449.0, 133.0)
[P: (56.0, 58.0), S: (449.0, 133.0)]
Text size:(436.0, 69.0)

I do see what you mean, in your case. But when I replicate your code I get this:

image

Which is perfect. I tried it with lots of other bits of text too. I printed this data too:

label size:(582, 37)
label rect:[P: (368, 225), S: (582, 37)]
Text size:(357, 23)
rect position:(368, 225)
rect size:(357, 23)

Snippet here:

func _ready():
	var font:Font = $Control/Label.get_theme_font("font")
	var text_size = font.get_multiline_string_size(
		$Control/Label.text,HORIZONTAL_ALIGNMENT_LEFT,
		$Control/Label.size.x,
		$Control/Label.get_theme_default_font_size(),
		$Control/Label.get_line_count())
	print("label size:", $Control/Label.size)
	print("label rect:", $Control/Label.get_rect())
	print("Text size:", text_size)
	
	$Control/ColorRect.position=$Control/Label.position
	$Control/ColorRect.size=text_size
	print("rect position:", $Control/ColorRect.position)
	print("rect size:", $Control/ColorRect.size)```

The only thing I noticed was that if I apply a theme directly to the label then it does not work, or apply label settings (here I changed the font size and font) like this:

I think it is because you get the default theme settings, not the label settings. So if you have set the label settings you need to use them, something like:


	var label_settings = $Control/Label.get("label_settings")
	var font_size = label_settings.get_font_size()
	print("label_settings font size:", font_size)

label_settings font size:27

The same applies if you have used a theme override, you have to get that and use that.

Hope that helps in some way.

PS The docs also mention that some kinds of fonts can cause problems. Might be worth double checking if it works with a normal, standard font file. It might be your font, although your font looks pretty ‘normal’ on the face of it TBH.

PPS I would guess you have increased your line-height in your Theme Overrides in the constants settings.

You should try multi-line text. I solved the problem and the entire solution doesn’t look so elegant, but it is crucial for customizing UI components.

var label_settings:LabelSettings = $Control/Label.get("label_settings")
var font_size = label_settings.get_font_size()
print("label_settings font size:", font_size)
var font:Font = $Control/Label.get_theme_font("font")
var text_size = font.get_multiline_string_size(
	$Control/Label.text,HORIZONTAL_ALIGNMENT_LEFT,
	$Control/Label.size.x,
	font_size,
	$Control/Label.get_line_count())
## Here is the key point: get_multiline_string_size does not calculate the line spacing.
text_size.y+=($Control/Label.get_line_count()*label_settings.line_spacing)
print($Control/Label.size)
print($Control/Label.get_rect())
print("Text size:", text_size)

$Control/ColorRect.position=$Control/Label.position
$Control/ColorRect.size=text_size