convert_hex_xterm (9403B)
1 #! /usr/bin/env python3 2 3 """ Convert values between RGB hex codes and xterm-256 color codes. 4 5 Nice long listing of all 256 colors and their codes. Useful for 6 developing console color themes, or even script output schemes. 7 8 Resources: 9 * http://en.wikipedia.org/wiki/8-bit_color 10 * http://en.wikipedia.org/wiki/ANSI_escape_code 11 * /usr/share/X11/rgb.txt 12 13 Credit goes to Micah Elliott - http://MicahElliott.com 14 --- 15 Script found at: https://gist.github.com/chrisdiana/c71f224626b5b9516bdb 16 Python 3 conversion and code improvements by Jake Bauer <jbauer@paritybit.ca> 17 """ 18 19 import sys, re 20 21 # Color Look-Up Table 22 # (8-bit, RGB hex) 23 CLUT = [ 24 # Primary 3-bit (8 colors). Unique representation! 25 ('00', '000000'), 26 ('01', '800000'), 27 ('02', '008000'), 28 ('03', '808000'), 29 ('04', '000080'), 30 ('05', '800080'), 31 ('06', '008080'), 32 ('07', 'c0c0c0'), 33 34 # Equivalent "bright" versions of original 8 colors. 35 ('08', '808080'), 36 ('09', 'ff0000'), 37 ('10', '00ff00'), 38 ('11', 'ffff00'), 39 ('12', '0000ff'), 40 ('13', 'ff00ff'), 41 ('14', '00ffff'), 42 ('15', 'ffffff'), 43 44 # Strictly ascending. 45 ('16', '000000'), 46 ('17', '00005f'), 47 ('18', '000087'), 48 ('19', '0000af'), 49 ('20', '0000d7'), 50 ('21', '0000ff'), 51 ('22', '005f00'), 52 ('23', '005f5f'), 53 ('24', '005f87'), 54 ('25', '005faf'), 55 ('26', '005fd7'), 56 ('27', '005fff'), 57 ('28', '008700'), 58 ('29', '00875f'), 59 ('30', '008787'), 60 ('31', '0087af'), 61 ('32', '0087d7'), 62 ('33', '0087ff'), 63 ('34', '00af00'), 64 ('35', '00af5f'), 65 ('36', '00af87'), 66 ('37', '00afaf'), 67 ('38', '00afd7'), 68 ('39', '00afff'), 69 ('40', '00d700'), 70 ('41', '00d75f'), 71 ('42', '00d787'), 72 ('43', '00d7af'), 73 ('44', '00d7d7'), 74 ('45', '00d7ff'), 75 ('46', '00ff00'), 76 ('47', '00ff5f'), 77 ('48', '00ff87'), 78 ('49', '00ffaf'), 79 ('50', '00ffd7'), 80 ('51', '00ffff'), 81 ('52', '5f0000'), 82 ('53', '5f005f'), 83 ('54', '5f0087'), 84 ('55', '5f00af'), 85 ('56', '5f00d7'), 86 ('57', '5f00ff'), 87 ('58', '5f5f00'), 88 ('59', '5f5f5f'), 89 ('60', '5f5f87'), 90 ('61', '5f5faf'), 91 ('62', '5f5fd7'), 92 ('63', '5f5fff'), 93 ('64', '5f8700'), 94 ('65', '5f875f'), 95 ('66', '5f8787'), 96 ('67', '5f87af'), 97 ('68', '5f87d7'), 98 ('69', '5f87ff'), 99 ('70', '5faf00'), 100 ('71', '5faf5f'), 101 ('72', '5faf87'), 102 ('73', '5fafaf'), 103 ('74', '5fafd7'), 104 ('75', '5fafff'), 105 ('76', '5fd700'), 106 ('77', '5fd75f'), 107 ('78', '5fd787'), 108 ('79', '5fd7af'), 109 ('80', '5fd7d7'), 110 ('81', '5fd7ff'), 111 ('82', '5fff00'), 112 ('83', '5fff5f'), 113 ('84', '5fff87'), 114 ('85', '5fffaf'), 115 ('86', '5fffd7'), 116 ('87', '5fffff'), 117 ('88', '870000'), 118 ('89', '87005f'), 119 ('90', '870087'), 120 ('91', '8700af'), 121 ('92', '8700d7'), 122 ('93', '8700ff'), 123 ('94', '875f00'), 124 ('95', '875f5f'), 125 ('96', '875f87'), 126 ('97', '875faf'), 127 ('98', '875fd7'), 128 ('99', '875fff'), 129 ('100', '878700'), 130 ('101', '87875f'), 131 ('102', '878787'), 132 ('103', '8787af'), 133 ('104', '8787d7'), 134 ('105', '8787ff'), 135 ('106', '87af00'), 136 ('107', '87af5f'), 137 ('108', '87af87'), 138 ('109', '87afaf'), 139 ('110', '87afd7'), 140 ('111', '87afff'), 141 ('112', '87d700'), 142 ('113', '87d75f'), 143 ('114', '87d787'), 144 ('115', '87d7af'), 145 ('116', '87d7d7'), 146 ('117', '87d7ff'), 147 ('118', '87ff00'), 148 ('119', '87ff5f'), 149 ('120', '87ff87'), 150 ('121', '87ffaf'), 151 ('122', '87ffd7'), 152 ('123', '87ffff'), 153 ('124', 'af0000'), 154 ('125', 'af005f'), 155 ('126', 'af0087'), 156 ('127', 'af00af'), 157 ('128', 'af00d7'), 158 ('129', 'af00ff'), 159 ('130', 'af5f00'), 160 ('131', 'af5f5f'), 161 ('132', 'af5f87'), 162 ('133', 'af5faf'), 163 ('134', 'af5fd7'), 164 ('135', 'af5fff'), 165 ('136', 'af8700'), 166 ('137', 'af875f'), 167 ('138', 'af8787'), 168 ('139', 'af87af'), 169 ('140', 'af87d7'), 170 ('141', 'af87ff'), 171 ('142', 'afaf00'), 172 ('143', 'afaf5f'), 173 ('144', 'afaf87'), 174 ('145', 'afafaf'), 175 ('146', 'afafd7'), 176 ('147', 'afafff'), 177 ('148', 'afd700'), 178 ('149', 'afd75f'), 179 ('150', 'afd787'), 180 ('151', 'afd7af'), 181 ('152', 'afd7d7'), 182 ('153', 'afd7ff'), 183 ('154', 'afff00'), 184 ('155', 'afff5f'), 185 ('156', 'afff87'), 186 ('157', 'afffaf'), 187 ('158', 'afffd7'), 188 ('159', 'afffff'), 189 ('160', 'd70000'), 190 ('161', 'd7005f'), 191 ('162', 'd70087'), 192 ('163', 'd700af'), 193 ('164', 'd700d7'), 194 ('165', 'd700ff'), 195 ('166', 'd75f00'), 196 ('167', 'd75f5f'), 197 ('168', 'd75f87'), 198 ('169', 'd75faf'), 199 ('170', 'd75fd7'), 200 ('171', 'd75fff'), 201 ('172', 'd78700'), 202 ('173', 'd7875f'), 203 ('174', 'd78787'), 204 ('175', 'd787af'), 205 ('176', 'd787d7'), 206 ('177', 'd787ff'), 207 ('178', 'd7af00'), 208 ('179', 'd7af5f'), 209 ('180', 'd7af87'), 210 ('181', 'd7afaf'), 211 ('182', 'd7afd7'), 212 ('183', 'd7afff'), 213 ('184', 'd7d700'), 214 ('185', 'd7d75f'), 215 ('186', 'd7d787'), 216 ('187', 'd7d7af'), 217 ('188', 'd7d7d7'), 218 ('189', 'd7d7ff'), 219 ('190', 'd7ff00'), 220 ('191', 'd7ff5f'), 221 ('192', 'd7ff87'), 222 ('193', 'd7ffaf'), 223 ('194', 'd7ffd7'), 224 ('195', 'd7ffff'), 225 ('196', 'ff0000'), 226 ('197', 'ff005f'), 227 ('198', 'ff0087'), 228 ('199', 'ff00af'), 229 ('200', 'ff00d7'), 230 ('201', 'ff00ff'), 231 ('202', 'ff5f00'), 232 ('203', 'ff5f5f'), 233 ('204', 'ff5f87'), 234 ('205', 'ff5faf'), 235 ('206', 'ff5fd7'), 236 ('207', 'ff5fff'), 237 ('208', 'ff8700'), 238 ('209', 'ff875f'), 239 ('210', 'ff8787'), 240 ('211', 'ff87af'), 241 ('212', 'ff87d7'), 242 ('213', 'ff87ff'), 243 ('214', 'ffaf00'), 244 ('215', 'ffaf5f'), 245 ('216', 'ffaf87'), 246 ('217', 'ffafaf'), 247 ('218', 'ffafd7'), 248 ('219', 'ffafff'), 249 ('220', 'ffd700'), 250 ('221', 'ffd75f'), 251 ('222', 'ffd787'), 252 ('223', 'ffd7af'), 253 ('224', 'ffd7d7'), 254 ('225', 'ffd7ff'), 255 ('226', 'ffff00'), 256 ('227', 'ffff5f'), 257 ('228', 'ffff87'), 258 ('229', 'ffffaf'), 259 ('230', 'ffffd7'), 260 ('231', 'ffffff'), 261 262 # Gray-scale range. 263 ('232', '080808'), 264 ('233', '121212'), 265 ('234', '1c1c1c'), 266 ('235', '262626'), 267 ('236', '303030'), 268 ('237', '3a3a3a'), 269 ('238', '444444'), 270 ('239', '4e4e4e'), 271 ('240', '585858'), 272 ('241', '626262'), 273 ('242', '6c6c6c'), 274 ('243', '767676'), 275 ('244', '808080'), 276 ('245', '8a8a8a'), 277 ('246', '949494'), 278 ('247', '9e9e9e'), 279 ('248', 'a8a8a8'), 280 ('249', 'b2b2b2'), 281 ('250', 'bcbcbc'), 282 ('251', 'c6c6c6'), 283 ('252', 'd0d0d0'), 284 ('253', 'dadada'), 285 ('254', 'e4e4e4'), 286 ('255', 'eeeeee'), 287 ] 288 289 def _create_dicts(): 290 short2rgb_dict = dict(CLUT) 291 rgb2short_dict = {} 292 for k, v in short2rgb_dict.items(): 293 rgb2short_dict[v] = k 294 return rgb2short_dict, short2rgb_dict 295 296 def short2rgb(short): 297 return SHORT2RGB_DICT[short] 298 299 def print_all(): 300 for short, rgb in CLUT: 301 sys.stdout.write('\033[48;5;%sm%s:%s' % (short, short, rgb)) 302 sys.stdout.write("\033[0m ") 303 sys.stdout.write('\033[38;5;%sm%s:%s' % (short, short, rgb)) 304 sys.stdout.write("\033[0m\n") 305 print("Printed all codes.") 306 print("You can translate a hex or 0-255 code by providing an argument.") 307 308 # Find the closest xterm-256 approximation to the given RGB value. 309 def rgb2short(rgb): 310 increments = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff) 311 312 # Break the 6-char RGB code into 3 integer values. 313 parts = [ int(h, 16) for h in re.split(r'(..)(..)(..)', rgb)[1:4] ] 314 result = [] 315 for part in parts: 316 i = 0 317 while i < len(increments)-1: 318 smaller, bigger = increments[i], increments[i+1] 319 if smaller <= part <= bigger: 320 smaller1 = abs(smaller - part) 321 bigger1 = abs(bigger - part) 322 if smaller1 < bigger1: 323 closest = smaller 324 else: 325 closest = bigger 326 result.append(closest) 327 break 328 i += 1 329 result = ''.join([ ('%02.x' % i) for i in result ]) 330 equivalent = RGB2SHORT_DICT[ result ] 331 return equivalent, result 332 333 RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts() 334 335 if __name__ == '__main__': 336 if len(sys.argv) == 1: 337 print_all() 338 sys.exit(0) 339 340 allowed_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 341 'a', 'b', 'c', 'd', 'e', 'f', '#'] 342 arg = sys.argv[1] 343 344 # Check for invalid characters 345 for char in arg: 346 if char not in allowed_chars: 347 print("Invalid character:", char) 348 print("Please input a valid 256-colour or hex code.") 349 sys.exit(1) 350 351 # Remove leading '#' 352 if arg.startswith('#'): 353 arg = arg.lstrip('#') 354 355 # 256-colour to hex 356 if len(arg) < 4 and int(arg) < 256: 357 rgb = short2rgb(arg) 358 sys.stdout.write('xterm color \033[38;5;%sm%s\033[0m -> RGB exact \033[38;5;%sm%s\033[0m' % (arg, arg, arg, rgb)) 359 sys.stdout.write("\033[0m\n") 360 361 # Hex to 256-colour 362 else: 363 # Convert an RGB value like f20 to ff2200 364 if len(arg) < 4: 365 arg = arg[0]*2 + arg[1]*2 + arg[2]*2 366 short, rgb = rgb2short(arg) 367 sys.stdout.write('RGB %s -> xterm color approx \033[38;5;%sm%s (%s)' % (arg, short, short, rgb)) 368 sys.stdout.write("\033[0m\n")