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