Having trouble with Image between Text

The Logo is not always between the Brackets


const function StripRichText(Text: string): string
	return (Text:gsub("<[^>]->", ""))
end

const function MeasureText(Text: string, TextSize: number): number
	const params = Instance.new("GetTextBoundsParams")
	params.Text = Text
	params.Size = TextSize
	params.Width = math.huge
	const bounds = TextService:GetTextBoundsAsync(params)
	params:Destroy()
	return bounds.X
end

const function PlaceLogoInBrackets(TextLabel: TextLabel, LogoID: string)
	const Logo: ImageLabel = TextLabel:FindFirstChild("Logo")
	if not Logo then
		return
	end

	const TextSize = TextLabel.TextSize
	const fullText = StripRichText(TextLabel.Text)

	const totalWidth = MeasureText(fullText, TextSize)

	const placeholderCenterX = -(totalWidth / 10) -- this is where the X position of the logo change

	Logo.AnchorPoint = Vector2.new(0.5, 0.5)
	Logo.Image = LogoID
	Logo.Position = UDim2.new(.5, placeholderCenterX, .5, 0)
	Logo.Visible = true
end

The problem’s this line:

local placeholderCenterX = -(totalWidth / 10)

totalWidth / 10 is just a magic number. It scales off the length of the whole string, so it only lines up for some names and drifts for anything longer or shorter. Nothing about it actually points at where the brackets are.

Just find the brackets and drop the logo in the middle of the gap:

local TextService = game:GetService("TextService")

local function StripRichText(text: string): string
	return (text:gsub("<[^>]->", ""))
end

local function MeasureText(text: string, textSize: number): number
	local params = Instance.new("GetTextBoundsParams")
	params.Text = text
	params.Size = textSize
	params.Width = math.huge
	local bounds = TextService:GetTextBoundsAsync(params)
	params:Destroy()
	return bounds.X
end

local function PlaceLogoInBrackets(label: TextLabel, logoId: string)
	local logo = label:FindFirstChild("Logo") :: ImageLabel?
	if not logo then
		return
	end

	local textSize = label.TextSize
	local fullText = StripRichText(label.Text)

	local openIndex = fullText:find("%[") -- % because [ ] are pattern specials
	local closeIndex = fullText:find("%]")
	if not openIndex or not closeIndex then
		logo.Visible = false
		return
	end

	local totalWidth = MeasureText(fullText, textSize)
	local openEdge = MeasureText(fullText:sub(1, openIndex), textSize)
	local closeEdge = MeasureText(fullText:sub(1, closeIndex), textSize) - MeasureText("]", textSize)
	local gapCenter = (openEdge + closeEdge) / 2

	local offsetX
	if label.TextXAlignment == Enum.TextXAlignment.Left then
		offsetX = gapCenter - label.AbsoluteSize.X / 2
	elseif label.TextXAlignment == Enum.TextXAlignment.Right then
		offsetX = gapCenter - totalWidth + label.AbsoluteSize.X / 2
	else
		offsetX = gapCenter - totalWidth / 2
	end

	logo.AnchorPoint = Vector2.new(0.5, 0.5)
	logo.Image = logoId
	logo.Position = UDim2.new(0.5, offsetX, 0.5, 0)
	logo.Visible = true
end

Lines up regardless of name length now.

still the same issues

I’ve looked into it again and I found that the drift isn’t the bracket math, it’s the measuring. GetTextBoundsAsync uses its own default font instead of the one your label renders in, so the widths come back wrong and the logo slides off. Same thing happens if the label has TextScaled on, since TextSize isn’t the real size at that point.

Measure in the label’s own font and rescale to the width it’s actually drawing (TextBounds):

local TextService = game:GetService("TextService")

local function StripRichText(text: string): string
	return (text:gsub("<[^>]->", ""))
end

local function MeasureText(text: string, font: Font, size: number): number
	local params = Instance.new("GetTextBoundsParams")
	params.Text = text
	params.Font = font
	params.Size = size
	params.Width = math.huge
	local bounds = TextService:GetTextBoundsAsync(params)
	params:Destroy()
	return bounds.X
end

local function PlaceLogoInBrackets(label: TextLabel, logoId: string)
	local logo = label:FindFirstChild("Logo") :: ImageLabel?
	if not logo then
		return
	end

	local fullText = StripRichText(label.Text)
	local openIndex = fullText:find("%[")
	local closeIndex = fullText:find("%]")
	if not openIndex or not closeIndex then
		logo.Visible = false
		return
	end

	local font = label.FontFace
	local size = label.TextSize

	-- measure at TextSize, then rescale to the real rendered width
	-- (fixes wrong font and TextScaled labels)
	local measured = MeasureText(fullText, font, size)
	local totalWidth = label.TextBounds.X
	local scale = totalWidth / measured

	local openEdge = MeasureText(fullText:sub(1, openIndex), font, size) * scale
	local closeEdge = (MeasureText(fullText:sub(1, closeIndex), font, size) - MeasureText("]", font, size)) * scale
	local gapCenter = (openEdge + closeEdge) / 2

	local offsetX
	if label.TextXAlignment == Enum.TextXAlignment.Left then
		offsetX = gapCenter - label.AbsoluteSize.X / 2
	elseif label.TextXAlignment == Enum.TextXAlignment.Right then
		offsetX = gapCenter - totalWidth + label.AbsoluteSize.X / 2
	else
		offsetX = gapCenter - totalWidth / 2
	end

	logo.AnchorPoint = Vector2.new(0.5, 0.5)
	logo.Image = logoId
	logo.Position = UDim2.new(0.5, offsetX, 0.5, 0)
	logo.Visible = true
end

One catch: TextBounds is only right after the label rendered a frame. If you call this straight after setting .Text, throw a task.wait() in front first.

Hey i found a solution, i’m using this lib

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.