I want a tight bounding box around rendered text. Labels are not tight enough

It’s possible to get that but it’s not simple.

Example:

extends Control

var paragraph = TextParagraph.new()

@export var font:Font

var text = "Hello world!\nAnother line.\nAnd another one that's long so it breaks whenever it needs to."

func _ready() -> void:
	# Add the text to the paragraph with the loaded font and with font size 32
	paragraph.add_string(text, font, 32)
	# Set the max width to 300
	paragraph.width = 300


func _draw() -> void:
	# Get the primary text server
	var text_server = TextServerManager.get_primary_interface()
	var x = 0.0
	var y = 0.0
	var ascent = 0.0
	var descent = 0.0
	# for each line
	for i in paragraph.get_line_count():
		# reset x
		x = 0.0
		# get the ascent and descent of the line
		ascent = paragraph.get_line_ascent(i)
		descent = paragraph.get_line_descent(i)

		# get the size of the line from the paragrah
		var line_size = paragraph.get_line_size(i)
		# prepare the tight rect
		var line_tight_rect = Rect2()
		# get the rid of the line
		var line_rid = paragraph.get_line_rid(i)
		# get all the glyphs that compose the line
		var glyphs = text_server.shaped_text_get_glyphs(line_rid)

		# for each glyph
		for glyph in glyphs:
			# Extract info about the glyph
			var glyph_font_rid = glyph.get('font_rid', RID())
			var glyph_font_size = Vector2i(glyph.get('font_size', 8), 0)
			var glyph_index = glyph.get('index', -1)
			var glyph_offset = text_server.font_get_glyph_offset(glyph_font_rid, glyph_font_size, glyph_index)
			var glyph_size = text_server.font_get_glyph_size(glyph_font_rid, glyph_font_size, glyph_index)
			# draw a red rect surrounding the glyph
			var glyph_rect = Rect2(Vector2(x, y + ascent) + glyph_offset, glyph_size)
			if glyph_rect.has_area():
				draw_rect(glyph_rect, Color.RED, false)
				if not line_tight_rect.has_area():
					# initialize the tight rect with the first glyph rect if it's empty
					line_tight_rect = glyph_rect
				else:
					# or merge the glyph rect
					line_tight_rect = line_tight_rect.merge(glyph_rect)
			# get the advance (how much the we need to move x)
			var advance = glyph.get("advance", 0)
			# add the advance to x
			x += advance

		# draw the tight rect
		draw_rect(line_tight_rect, Color(0, 1, 0, 0.4))
		# draw the size of the line from the paragraph
		draw_rect(Rect2(Vector2(0, y), line_size), Color.BLUE, false)

		# update y with the ascent and descent of the line
		y += ascent + descent

	# draw the paragraph to this canvas item
	paragraph.draw(get_canvas_item(), Vector2.ZERO)

Result:

Red: The bounding box of the glyph
Green: The tight bounding box of the line
Blue: The bounding box of the line

3 Likes