Need help getting RichText formatting colors from a TextLabel onto multiple ImageLabels that represent each letter

It cuts off the k in the first bit of text, but works fine on other text without a k

1 Like

specifically the second k gets cut off weirdly enough

thereā€™s also supposed to be an ! after it

local function applyRichTextColors(richText, frameWithImageLabels)
    local function parseAndApply()
        local plainText = ""
        local colorSegments = {}
        local currentIndex = 1

        local cleanText = richText:gsub("<[^>]+>", "")

        while currentIndex <= #richText do
            local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
            if not tagStart then
                plainText = plainText .. cleanText:sub(currentIndex)
                break
            end

            plainText = plainText .. cleanText:sub(currentIndex, tagStart - 1)
            
            local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
            local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

            local contentStart = tagEnd + 1
            local contentEnd = richText:find("</font>", contentStart, true)
            if not contentEnd then 
                plainText = plainText .. cleanText:sub(contentStart)
                break 
            end
            
            local content = cleanText:sub(#plainText + 1, #plainText + (contentEnd - contentStart))

            table.insert(colorSegments, {
                startPosition = #plainText + 1,
                endPosition = #plainText + #content,
                color = color
            })

            plainText = plainText .. content
            currentIndex = contentEnd + 7
        end

        return plainText, colorSegments
    end

    local plainText, colorSegments = parseAndApply()

    if frameWithImageLabels then
        for _, segment in ipairs(colorSegments) do
            for i = segment.startPosition, segment.endPosition do
                local frame = frameWithImageLabels:FindFirstChild(tostring(i))
                if frame and frame:IsA("Frame") then
                    local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
                    if imageLabel then
                        imageLabel.ImageColor3 = segment.color
                        print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
                    end
                end
            end
        end
    end

    print("Clean text length:", #richText:gsub("<[^>]+>", ""))
    print("Plain text length:", #plainText)
    print("Final plain text (should have no HTML tags):", plainText)

    return plainText
end

local richText = '<font color="rgb(255,0,0)">Welcome to Shork</font> <font color="rgb(0,255,0)">Shipwreck!</font>'
local frameWithImageLabels = -- Your frame containing ImageLabels
local plainText = applyRichTextColors(richText, frameWithImageLabels)
print("Plain text:", plainText)

for match in richText:gmatch("rgb%((%d+),%s*(%d+),%s*(%d+)%)") do
    print("RGB values found:", match)
end

not sure whatā€™s the issue i cleaned the html tags again

1 Like

Itā€™s no longer coloring the text, weirdly enough, and itā€™s still cutting off the k! in the first string of text

hopefully the output in the video helps you solve the issue

i think i got the issue probably because the contentend math

local function applyRichTextColors(richText, frameWithImageLabels)
    local function parseAndApply()
        local plainText = ""
        local colorSegments = {}
        local currentIndex = 1

        local cleanText = richText:gsub("<[^>]+>", "")

        while currentIndex <= #richText do
            local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
            if not tagStart then
                plainText = plainText .. cleanText:sub(currentIndex)
                break
            end

            plainText = plainText .. cleanText:sub(currentIndex, tagStart - 1)
            
            local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
            local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

            local contentStart = tagEnd + 1
            local contentEnd = richText:find("</font>", contentStart, true)
            if not contentEnd then 
                contentEnd = #richText
            else
                contentEnd = contentEnd - 1
            end
            
            local content = cleanText:sub(#plainText + 1, #plainText + (contentEnd - contentStart) + 1)

            table.insert(colorSegments, {
                startPosition = #plainText + 1,
                endPosition = #plainText + #content,
                color = color
            })

            plainText = plainText .. content
            currentIndex = contentEnd + 7
        end

        return plainText, colorSegments
    end

    local plainText, colorSegments = parseAndApply()

    if frameWithImageLabels then
        for _, segment in ipairs(colorSegments) do
            for i = segment.startPosition, segment.endPosition do
                local frame = frameWithImageLabels:FindFirstChild(tostring(i))
                if frame and frame:IsA("Frame") then
                    local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
                    if imageLabel then
                        imageLabel.ImageColor3 = segment.color
                        print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
                    end
                end
            end
        end
    end

    print("Clean text:", cleanText)
    print("Plain text:", plainText)
    print("Number of color segments:", #colorSegments)
    
    return plainText
end

local richText = '<font color="rgb(160,97,0)">Welcome to Shork</font> <font color="rgb(0,255,0)">Shipwreck!</font>'
local frameWithImageLabels = -- Your frame containing ImageLabels
local plainText = applyRichTextColors(richText, frameWithImageLabels)

for match in richText:gmatch("rgb%((%d+),%s*(%d+),%s*(%d+)%)") do
    print("RGB values found:", match)
end

if that doesnā€™t work let me know

1 Like

also I should clarify I have it run it once with the frame inputted as nil in order to return just the unformatted text, and I then feed that unformatted text to my text generator, and once the frame is created I call your function again this time sending the frame to it
Hereā€™s what that looks like

image

1 Like

you forgot to return the variable for cleanText so it can properly print it, so I fixed that real quick, Iā€™ll give it a test run now and send the results

local function applyRichTextColors(richText, frameWithImageLabels)
	local function parseAndApply()
		local cleanText = richText:gsub("<[^>]+>", "")
		local plainText = ""
		local colorSegments = {}
		local currentIndex = 1

		while currentIndex <= #richText do
			local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
			if not tagStart then
				plainText = plainText .. cleanText:sub(currentIndex)
				break
			end

			plainText = plainText .. cleanText:sub(currentIndex, tagStart - 1)

			local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
			local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

			local contentStart = tagEnd + 1
			local contentEnd = richText:find("</font>", contentStart, true)
			if not contentEnd then 
				contentEnd = #richText
			else
				contentEnd = contentEnd - 1
			end

			local content = cleanText:sub(#plainText + 1, #plainText + (contentEnd - contentStart) + 1)

			table.insert(colorSegments, {
				startPosition = #plainText + 1,
				endPosition = #plainText + #content,
				color = color
			})

			plainText = plainText .. content
			currentIndex = contentEnd + 7
		end

		return cleanText, plainText, colorSegments
	end

	local cleanText, plainText, colorSegments = parseAndApply()

	if frameWithImageLabels then
		for _, segment in ipairs(colorSegments) do
			for i = segment.startPosition, segment.endPosition do
				local frame = frameWithImageLabels:FindFirstChild(tostring(i))
				if frame and frame:IsA("Frame") then
					local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
					if imageLabel then
						imageLabel.ImageColor3 = segment.color
						print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
					end
				end
			end
		end
	end

	print("Clean text:", cleanText)
	print("Plain text:", plainText)
	print("Number of color segments:", #colorSegments)

	return plainText
end
1 Like

it looks like it got the cleanText right but still gets the plainText wrong

1 Like

Alright
i can give it one more try or two because i am really confused i added a lot of debugging please copy me the results in case doesnā€™t work

local function applyRichTextColors(richText, frameWithImageLabels)
    local function parseAndApply()
        local plainText = ""
        local colorSegments = {}
        local currentIndex = 1

        local cleanText = richText:gsub("<[^>]+>", "")

        while currentIndex <= #richText do
            local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
            if not tagStart then
                plainText = plainText .. cleanText:sub(currentIndex)
                break
            end

            plainText = plainText .. cleanText:sub(currentIndex, #plainText + (tagStart - currentIndex))
            
            local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
            local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

            local contentStart = tagEnd + 1
            local contentEnd = richText:find("</font>", contentStart, true)
            if not contentEnd then 
                contentEnd = #richText
            else
                contentEnd = contentEnd - 1
            end
            
            local content = cleanText:sub(#plainText + 1, #plainText + (contentEnd - contentStart) + 1)

            table.insert(colorSegments, {
                startPosition = #plainText + 1,
                endPosition = #plainText + #content,
                color = color
            })

            plainText = plainText .. content
            currentIndex = contentEnd + 7
        end

        return cleanText, plainText, colorSegments
    end

    local cleanText, plainText, colorSegments = parseAndApply()

    if frameWithImageLabels then
        for _, segment in ipairs(colorSegments) do
            for i = segment.startPosition, segment.endPosition do
                local frame = frameWithImageLabels:FindFirstChild(tostring(i))
                if frame and frame:IsA("Frame") then
                    local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
                    if imageLabel then
                        imageLabel.ImageColor3 = segment.color
                        print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
                    end
                end
            end
        end
    end

    print("Original rich text:", richText)
    print("Clean text:", cleanText)
    print("Plain text:", plainText)
    print("Number of color segments:", #colorSegments)
    
    -- Additional checks
    assert(#cleanText == #plainText, "Clean text and plain text lengths don't match")
    assert(cleanText == plainText, "Clean text and plain text content don't match")
    
    for i, segment in ipairs(colorSegments) do
        print("Segment", i, "Start:", segment.startPosition, "End:", segment.endPosition, "Color:", segment.color)
        assert(segment.startPosition <= segment.endPosition, "Invalid segment range")
        assert(segment.endPosition <= #plainText, "Segment end exceeds text length")
    end
    
    return plainText, cleanText, colorSegments
end

local richText = '<font color="rgb(160,97,0)">Welcome to Shork</font> <font color="rgb(0,255,0)">Shipwreck!</font>'
local frameWithImageLabels = yourframe -- your frame here
local plainText, cleanText, colorSegments = applyRichTextColors(richText, frameWithImageLabels)

print("\nFinal results:")
print("Clean text:", cleanText)
print("Plain text:", plainText)

for match in richText:gmatch("rgb%((%d+),%s*(%d+),%s*(%d+)%)") do
    print("RGB values found:", match)
end
1 Like

Hereā€™s the result

local function applyRichTextColors(richText, frameWithImageLabels)
    local function parseAndApply()
        local plainText = ""
        local colorSegments = {}
        local currentIndex = 1

        local cleanText = richText:gsub("<[^>]+>", "")

        while currentIndex <= #richText do
            local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
            if not tagStart then
                plainText = plainText .. richText:sub(currentIndex):gsub("</?font>", "")
                break
            end

            plainText = plainText .. richText:sub(currentIndex, tagStart - 1):gsub("</?font>", "")
            
            local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
            local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

            local contentStart = tagEnd + 1
            local contentEnd = richText:find("</font>", contentStart, true)
            if not contentEnd then 
                contentEnd = #richText
            else
                contentEnd = contentEnd - 1
            end
            
            local content = richText:sub(contentStart, contentEnd):gsub("</?font>", "")

            table.insert(colorSegments, {
                startPosition = #plainText + 1,
                endPosition = #plainText + #content,
                color = color
            })

            plainText = plainText .. content
            currentIndex = contentEnd + 7
        end

        return cleanText, plainText, colorSegments
    end

    local cleanText, plainText, colorSegments = parseAndApply()

    if frameWithImageLabels then
        for _, segment in ipairs(colorSegments) do
            for i = segment.startPosition, segment.endPosition do
                local frame = frameWithImageLabels:FindFirstChild(tostring(i))
                if frame and frame:IsA("Frame") then
                    local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
                    if imageLabel then
                        imageLabel.ImageColor3 = segment.color
                        print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
                    end
                end
            end
        end
    end

    print("Original rich text:", richText)
    print("Clean text:", cleanText)
    print("Plain text:", plainText)
    print("Number of color segments:", #colorSegments)
    
    assert(#cleanText == #plainText, "Clean text and plain text lengths don't match")
    assert(cleanText == plainText, "Clean text and plain text content don't match")
    
    for i, segment in ipairs(colorSegments) do
        print("Segment", i, "Start:", segment.startPosition, "End:", segment.endPosition, "Color:", segment.color)
    end
    
    return plainText, cleanText, colorSegments
end

local richText = 'Welcome to <font color="rgb(160, 193, 255)">Shork</font> <font color="rgb(97, 134, 255)">Shipwreck</font>!'
local frameWithImageLabels = yourframehere
local plainText, cleanText, colorSegments = applyRichTextColors(richText, frameWithImageLabels)

print("\nFinal results:")
print("Clean text:", cleanText)
print("Plain text:", plainText)

for match in richText:gmatch("rgb%((%d+),%s*(%d+),%s*(%d+)%)") do
    print("RGB values found:", match)
end

Alrgiht could you try again and give me the results in case doesnā€™t work?

1 Like

soooo close, now it has the > and > back again in the plainText but no longer cuts off the k!

1 Like

good to hear i updated the code again could you try ? give me the output in-case

local function applyRichTextColors(richText, frameWithImageLabels)
    local function parseAndApply()
        local plainText = ""
        local colorSegments = {}
        local currentIndex = 1

        local cleanText = richText:gsub("<[^>]+>", "")

        while currentIndex <= #richText do
            local tagStart, tagEnd = richText:find("<font%s+color=\"rgb%((%d+),%s*(%d+),%s*(%d+)%)\">", currentIndex)
            if not tagStart then
                plainText = plainText .. richText:sub(currentIndex):gsub("</?font>", ""):gsub("[<>]", "")
                break
            end

            plainText = plainText .. richText:sub(currentIndex, tagStart - 1):gsub("</?font>", ""):gsub("[<>]", "")
            
            local r, g, b = richText:match("(%d+),%s*(%d+),%s*(%d+)", tagStart)
            local color = Color3.fromRGB(tonumber(r), tonumber(g), tonumber(b))

            local contentStart = tagEnd + 1
            local contentEnd = richText:find("</font>", contentStart, true)
            if not contentEnd then 
                contentEnd = #richText
            else
                contentEnd = contentEnd - 1
            end
            
            local content = richText:sub(contentStart, contentEnd):gsub("</?font>", ""):gsub("[<>]", "")

            table.insert(colorSegments, {
                startPosition = #plainText + 1,
                endPosition = #plainText + #content,
                color = color
            })

            plainText = plainText .. content
            currentIndex = contentEnd + 7
        end

        return cleanText, plainText, colorSegments
    end

    local cleanText, plainText, colorSegments = parseAndApply()

    if frameWithImageLabels then
        for _, segment in ipairs(colorSegments) do
            for i = segment.startPosition, segment.endPosition do
                local frame = frameWithImageLabels:FindFirstChild(tostring(i))
                if frame and frame:IsA("Frame") then
                    local imageLabel = frame:FindFirstChildOfClass("ImageLabel")
                    if imageLabel then
                        imageLabel.ImageColor3 = segment.color
                        print("Applied color", segment.color, "to letter at position", i, "frame", frame.Name)
                    end
                end
            end
        end
    end

    print("Original rich text:", richText)
    print("Clean text:", cleanText)
    print("Plain text:", plainText)
    print("Number of color segments:", #colorSegments)
    
    assert(#cleanText == #plainText, "Clean text and plain text lengths don't match")
    assert(cleanText == plainText, "Clean text and plain text content don't match")
    
    for i, segment in ipairs(colorSegments) do
        print("Segment", i, "Start:", segment.startPosition, "End:", segment.endPosition, "Color:", segment.color)
    end
    
    return plainText, cleanText, colorSegments
end
1 Like

It works flawlessly now, thank you SO MUCH!

Hereā€™s what it printed when I ran it on the first piece of dialog

and here it is in action

1 Like

There is one flaw with it, which is that it doesnā€™t support formatting within formatting, but itā€™s easy to work around, I just had to add some conditional statements to my formatting code to make sure thereā€™s not formatting within formatting (for example formatting on shork then formatting on the s, and the formatting for the s also wraps around the formatting for the shork), solved it by checking for one or the other instead of formatting both shork and shorks, very easy.

Once again thanks for the code, it works well enough for what Iā€™ll use it for! Iā€™m mainly just mentioning this detail for anyone else who might find this post useful in the future.

1 Like

I also did some more work on my dialog system and Iā€™m really proud of how itā€™s turning out

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