Parse a html color "#rrggbb", extract the RGB values

A customer of my scriptable clipboard recorder (iClip) would like to see the recorded clips be shown in the HTML color if a text like #EDCB43 was copied to the clipboard.

So I need to parse the string, and I like to do that efficiently. Are there built-in functions for this? I could implement something using the ObjC bridge, like suggested here, but that’s rather tedious.

I’ve gotten as far as getting all chars of the string, and checking for the leading “#”. I could then get every of the 6 other chars one by one, checking if they’re in the range of 0-9 or their uppercase is A-F, and then calculate their numeric value, i.e. do that all very low level in AS. Are there smarter ways?

For instance, if the input is “#112233”, I like to get three decimal integer values out: 17, 34 and 51.

I came up with a manual way in the mean time, but I like Fredrik71’s first approach better because it’s more concise.

set rgb1 to my rgbValuesOfHtmlColor("#FA9")
set rgb2 to my rgbValuesOfHtmlColor("#112233")

on rgbValuesOfHtmlColor(theText)
	local rgb, itsLength, hexChars, partWidth, part, startPos
	local partChars, aDigit, partValue, oneDigit, charCode
	
	set rgb to {}
	
	if first character of theText is "#" then
		set itsLength to length of theText
		if itsLength is 4 or itsLength is 7 then
			-- now we scan the 3 or 6 chars, decoding them as hex digits
			set hexChars to characters 2 thru -1 of theText
			if itsLength is 4 then
				set partWidth to 1
			else
				set partWidth to 2
			end if
			repeat with part from 1 to 3 -- iterate over each of the R, G and B parts
				set startPos to 1 + (part - 1) * partWidth
				set partChars to items startPos thru (startPos + partWidth - 1) of hexChars
				if length of partChars is 1 then
					-- if it's a 3-digit code, then double each value (F becomes FF)
					set end of partChars to first item of partChars
				end if
				-- now we have two hex digits that we decode into a value between 0 and 255
				set partValue to 0
				repeat with oneDigit in partChars
					set partValue to partValue * 16
					set charCode to id of oneDigit
					if charCode ≥ 48 and charCode ≤ 57 then
						-- "0" to "9"
						set partValue to partValue + (charCode - 48)
					else if charCode ≥ 65 and charCode ≤ 70 then
						-- "A" to "F"
						set partValue to partValue + (charCode - 55)
					else if charCode ≥ 97 and charCode ≤ 102 then
						-- "a" to "f"
						set partValue to partValue + (charCode - 87)
					else
						-- invalid hex digit
						return {}
					end if
				end repeat
				set end of rgb to partValue
			end repeat
		end if
	end if
	
	return rgb
end rgbValuesOfHtmlColor

I’ve also made a version based on Fredrik71’s solution:

use framework "Foundation"

set rgb1 to my rgbValuesOfHtmlColor("#FA9")
set rgb2 to my rgbValuesOfHtmlColor("#112233")

on rgbValuesOfHtmlColor(theText)
	local r, g, b, itsLength
	
	if first character of theText is "#" then
		set itsLength to length of theText
		if itsLength is 4 then
			set {theResult, r} to ((current application's NSScanner's scannerWithString:((character 2 of theText) & (character 2 of theText)))'s scanHexInt:(reference))
			if not theResult then return {}
			set {theResult, g} to ((current application's NSScanner's scannerWithString:((character 3 of theText) & (character 3 of theText)))'s scanHexInt:(reference))
			if not theResult then return {}
			set {theResult, b} to ((current application's NSScanner's scannerWithString:((character 4 of theText) & (character 4 of theText)))'s scanHexInt:(reference))
			if not theResult then return {}
		else if itsLength is 7 then
			set {theResult, r} to ((current application's NSScanner's scannerWithString:(text 2 thru 3 of theText))'s scanHexInt:(reference))
			if not theResult then return {}
			set {theResult, g} to ((current application's NSScanner's scannerWithString:(text 4 thru 5 of theText))'s scanHexInt:(reference))
			if not theResult then return {}
			set {theResult, b} to ((current application's NSScanner's scannerWithString:(text 6 thru 7 of theText))'s scanHexInt:(reference))
			if not theResult then return {}
		end if
		return {r, g, b}
	end if
	
	return rgb
end rgbValuesOfHtmlColor
1 Like

Why do you use a tuple {theResult, theRed} in the assignments and not just assign to theRed?

Ohhh - because theResult is the function return value and theRed is the passed (automatically created) reference. Wow, that’s handy.