summaryrefslogtreecommitdiff
path: root/color-converter.py
diff options
context:
space:
mode:
authorSimon <vesspa0330@gmail.com>2025-01-20 13:35:03 -0500
committerSimon <vesspa0330@gmail.com>2025-01-20 13:35:03 -0500
commit0d0f27b944d701553ec0e6fdc7885bb4a3297b82 (patch)
treee08234a13d1c34bf4ecc5ecb930a34e1d1177d03 /color-converter.py
parent22838a24da521ff049826f5e94e9f61867e5127e (diff)
overhauled format handler functions to be more generic / easily
expandable. all coversions functions have been verified. file input sanitation now more robust.
Diffstat (limited to 'color-converter.py')
-rw-r--r--color-converter.py457
1 files changed, 235 insertions, 222 deletions
diff --git a/color-converter.py b/color-converter.py
index 9f5620d..a0123e0 100644
--- a/color-converter.py
+++ b/color-converter.py
@@ -1,30 +1,24 @@
import math
import argparse
-# Order of types must match order of arguments defined
-# (or else arg validation will no longer work properly)
TYPES = ['hex', 'rgb', 'cmy', 'cmyk', 'hsl', 'hsv']
# look into LAB
HEX_LETTERS = ['a', 'b', 'c', 'd', 'e', 'f']
OUTPUT = 'stdout'
-APPEND = False
VERBOSE = False
def main():
- # Set up arguments
- parser = argparse.ArgumentParser(prog='color-converter',
- description='Color code converting utility written in Python.',
- epilog='Hope this helps :)')
-
+ ''' ESTABLISH ARGS '''
+ parser = argparse.ArgumentParser(prog='color-converter', description='Color code converting utility written in Python.', epilog='Hope this helps :)')
parser.add_argument('-hex', action='store_true', help='convert/output to Hex')
parser.add_argument('-rgb', action='store_true', help='convert/output to RGB')
parser.add_argument('-cmy', action='store_true', help='convert/output to CMY')
- parser.add_argument('-cmyk', action='store_true', help='convert/output to CMYK')
+ parser.add_argument('-cmyk', action='store_true', help='convert/output to CMYK (CMYK conversions are uncalibrated)')
parser.add_argument('-hsl', action='store_true', help='convert/output to HSL')
parser.add_argument('-hsv', action='store_true', help='convert/output to HSV')
@@ -36,19 +30,15 @@ def main():
parser.add_argument('-isHsv', action='store_true', help='indicate that inputted value(s) will be sets of HSL codes')
parser.add_argument('--input', '-i', help='name of the input file containing color codes to process')
- parser.add_argument('--output', '-o', help='the name of the file to store output in (will create file if doesn\'t exist, will OVERWRITE existing file\'s contents)')
- parser.add_argument('--append', '-a', action='store_true', help='append rather than overwrite file output')
+ parser.add_argument('--output', '-o', help='the name of a file to store output in (will create file if doesn\'t exist, will OVERWRITE existing file\'s contents)')
+ parser.add_argument('--append', '-a', action='store_true', help='append rather than overwrite when outputting to file rather than stdout')
parser.add_argument('--verbose', '-v', action='store_true', help='print when performing conversions')
- parser.add_argument('color', nargs='*', help='accepts a color in Hex, RGB, CMY, CMYK, or HSL and performs format conversions (does not support CMYK profiles, conversions are uncalibrated)')
+ parser.add_argument('color', nargs='*', help='a color in Hex, RGB, CMY, CMYK, HSL or HSV format')
args = parser.parse_args()
- # debug print
- # print(vars(args))
-
-
- ''' ARGS PROCESSING '''
+ ''' PROCESS ARGS '''
# determine which conversions to perform
outputFormats = []
flagsActive = 0
@@ -62,13 +52,14 @@ def main():
for colorFormat in TYPES :
outputFormats.append(colorFormat)
- # if output should be written to file, set global vars for reference later
+ # determine if output should be (over?)written to file
if args.output :
global OUTPUT
OUTPUT = args.output
- if args.append :
- global APPEND
- APPEND = True
+ if not args.append :
+ output_file = open(OUTPUT, 'w')
+ output_file.write('')
+ output_file.close()
# print conversion updates?
if args.verbose :
@@ -76,20 +67,20 @@ def main():
VERBOSE = True
-
''' PARSE INPUTTED COLORS '''
colorCodes = []
# if provided a file of values
if args.input :
with open(args.input, 'r', encoding='utf-8') as file :
for line in file :
- colorCodes.append(line.strip())
+ line = line.strip()
+ if line != '' :
+ colorCodes.append(line)
# else grab from stdin
else :
colorCodes = args.color
-
''' PROCESS COLORS '''
for color in colorCodes :
# Try to automatically handle value
@@ -112,180 +103,169 @@ def main():
else :
print('ERROR: Could not detect inputted color format and no fallback flag was specified. see --help for more information on usage.')
return
-
return
-
-##
+'''
# FORMAT HANDLERS
-##
-
+'''
# ARGS
# color: string containing hex code
-# outputFormats: list indicating which conversions to perform
+# outputFormats: list indicating which conversions to perform
def handleHex(color, outputFormats) :
- hexCode = validateHex(color)
- if hexCode is None :
+ # validate/sanitize input
+ hexcode = validateHex(color)
+ if hexcode is None :
return
+ # collect conversion results
+ results = {}
if VERBOSE :
- print('CONVERTING HEX: ', hexCode)
+ results['verbose-msg'] = 'CONVERTING HEX: ' + hexcode
- outputs = {}
- rgbValues = HEXtoRGB(hexCode)
- if 'hex' in outputFormats :
- outputs['hex'] = hexCode
- if 'rgb' in outputFormats :
- outputs['rgb'] = rgbValues
- if 'cmy' in outputFormats :
- outputs['cmy'] = RGBtoCMY(rgbValues)
- if 'cmyk' in outputFormats :
- outputs['cmyk'] = RGBtoCMYK(rgbValues)
- if 'hsl' in outputFormats :
- outputs['hsl'] = RGBtoHSVorHSL(rgbValues, 'hsl')
- if 'hsv' in outputFormats :
- outputs['hsv'] = RGBtoHSVorHSL(rgbValues, 'hsv')
+ rgbValues = HEXtoRGB(hexcode)
+ for colorFormat in TYPES :
+ if colorFormat in outputFormats and colorFormat == 'hex' :
+ results['hex'] = hexcode
+ elif colorFormat in outputFormats and colorFormat == 'rgb' :
+ results['rgb'] = rgbValues
+ elif colorFormat in outputFormats :
+ results[colorFormat] = rgbTo(colorFormat, rgbValues)
- printConversions(outputs)
+ printConversions(results)
# ARGS
# color: string containing RGB code
# outputFormats: list indicating which conversions to perform
def handleRGB(color, outputFormats) :
+ # validate/sanitize input
rgbValues = validateRGB(color)
if rgbValues is None :
return
+ # collect conversion results
+ results = {}
if VERBOSE :
- print('CONVERTING RGB: ', rgbValues)
+ results['verbose-msg'] = 'CONVERTING RGB: ' + str(rgbValues)
- outputs = {}
- if 'hex' in outputFormats :
- outputs['hex'] = RGBtoHEX(rgbValues)
- if 'rgb' in outputFormats :
- outputs['rgb'] = rgbValues
- if 'cmy' in outputFormats :
- outputs['cmy'] = RGBtoCMY(rgbValues)
- if 'cmyk' in outputFormats :
- outputs['cmyk'] = RGBtoCMYK(rgbValues)
- if 'hsl' in outputFormats :
- outputs['hsl'] = RGBtoHSVorHSL(rgbValues, 'hsl')
- if 'hsv' in outputFormats :
- outputs['hsv'] = RGBtoHSVorHSL(rgbValues, 'hsv')
+ for colorFormat in TYPES :
+ if colorFormat in outputFormats and colorFormat == 'rgb' :
+ results['rgb'] = rgbValues
+ elif colorFormat in outputFormats :
+ results[colorFormat] = rgbTo(colorFormat, rgbValues)
- printConversions(outputs)
+ printConversions(results)
# ARGS
# color: string containing CMY code
# outputFormats: list indicating which conversions to perform
def handleCMY(color, outputFormats) :
+ # validate/sanitize input
cmyValues = validateCMYorCMYK(color, False)
if cmyValues is None :
return
+ # collect conversion results
+ results = {}
if VERBOSE :
- print('CONVERTING CMY: ', cmyValues)
+ results['verbose-msg'] = 'CONVERTING CMY: ' + str(cmyValues)
- outputs = {}
rgbValues = CMYtoRGB(cmyValues)
- if 'hex' in outputFormats :
- outputs['hex'] = RGBtoHEX(rgbValues)
- if 'rgb' in outputFormats :
- outputs['rgb'] = rgbValues
- if 'cmy' in outputFormats :
- outputs['cmy'] = cmyValues
- if 'cmyk' in outputFormats :
- outputs['cmyk'] = RGBtoCMYK(rgbValues)
- if 'hsl' in outputFormats :
- outputs['hsl'] = RGBtoHSVorHSL(rgbValues, 'hsl')
- if 'hsv' in outputFormats :
- outputs['hsv'] = RGBtoHSVorHSL(rgbValues, 'hsv')
+ for colorFormat in TYPES :
+ if colorFormat in outputFormats and colorFormat == 'rgb' :
+ results['rgb'] = rgbValues
+ elif colorFormat in outputFormats and colorFormat == 'cmy' :
+ results['cmy'] = cmyValues
+ elif colorFormat in outputFormats :
+ results[colorFormat] = rgbTo(colorFormat, rgbValues)
- printConversions(outputs)
+ printConversions(results)
# ARGS
# color: string containing CMYK code
# outputFormats: list indicating which conversions to perform
def handleCMYK(color, outputFormats) :
+ # validate/sanitize input
cmykValues = validateCMYorCMYK(color, True)
if cmykValues is None :
return
+ # collect conversion results
+ results = {}
if VERBOSE :
- print('CONVERTING CMYK: ', cmykValues)
+ results['verbose-msg'] = 'CONVERTING CMYK: ' + str(cmykValues)
- outputs = {}
rgbValues = CMYKtoRGB(cmykValues)
- if 'hex' in outputFormats :
- outputs['hex'] = RGBtoHEX(rgbValues)
- if 'rgb' in outputFormats :
- outputs['rgb'] = rgbValues
- if 'cmy' in outputFormats :
- outputs['cmy'] = RGBtoCMY(rgbValues)
- if 'cmyk' in outputFormats :
- outputs['cmyk'] = cmykValues
- if 'hsl' in outputFormats :
- outputs['hsl'] = RGBtoHSVorHSL(rgbValues, 'hsl')
- if 'hsv' in outputFormats :
- outputs['hsv'] = RGBtoHSVorHSL(rgbValues, 'hsv')
+ for colorFormat in TYPES :
+ if (colorFormat in outputFormats) and (colorFormat == 'rgb') :
+ results['rgb'] = rgbValues
+ elif (colorFormat in outputFormats) and (colorFormat == 'cmyk') :
+ results['cmyk'] = cmykValues
+ elif colorFormat in outputFormats :
+ results[colorFormat] = rgbTo(colorFormat, rgbValues)
- printConversions(outputs)
+ printConversions(results)
# ARGS
# color: string containing CMY code
# outputFormats: list indicating which conversions to perform
# handle: string ('hsl' or 'hsv'), indicating which format to handle
def handleHSVorHSL(color, handle, outputFormats) :
+ # collect conversion results
validated = validateHSLorHSV(color)
if validated is None :
return
+ # collect conversion results
+ results = {}
if VERBOSE :
if handle == 'hsl' :
- print('CONVERTING HSL: ', color)
+ results['verbose-msg'] = 'CONVERTING HSL: ' + str(validated)
else :
- print('CONVERTING HSV: ', color)
+ results['verbose-msg'] = 'CONVERTING HSV: ' + str(validated)
- outputs = {}
rgbValues = HSLorHSVToRGB(validated, handle)
- if 'hex' in outputFormats :
- outputs['hex'] = RGBtoHEX(rgbValues)
- if 'rgb' in outputFormats :
- outputs['rgb'] = rgbValues
- if 'cmy' in outputFormats :
- outputs['cmy'] = RGBtoCMY(rgbValues)
- if 'cmyk' in outputFormats :
- outputs['cmyk'] = RGBtoCMYK(rgbValues)
- if 'hsl' in outputFormats :
- outputs['hsl'] = RGBtoHSVorHSL(rgbValues, 'hsl')
- if 'hsv' in outputFormats :
- outputs['hsv'] = RGBtoHSVorHSL(rgbValues, 'hsv')
+ for colorFormat in TYPES :
+ if (colorFormat in outputFormats) and (colorFormat == 'rgb') :
+ results['rgb'] = rgbValues
+ elif (colorFormat in outputFormats) and (colorFormat == 'hsl') and (handle == 'hsl') :
+ results['hsl'] = validated
+ elif (colorFormat in outputFormats) and (colorFormat == 'hsv') and (handle == 'hsv'):
+ results['hsv'] = validated
+ elif colorFormat in outputFormats :
+ results[colorFormat] = rgbTo(colorFormat, rgbValues)
- printConversions(outputs)
+ printConversions(results)
-##
-# CONVERSION FUNCTIONS
-##
+'''
+# CONVERSION FUNCTIONS
+'''
-# Takes in a string, returns a list of 3 integers
-def HEXtoRGB(hexCode) :
- rgbValues = []
+##
+# RGB -> OTHER FORMATS SECTION
+###
- tempSum = 0
- i = 0
- while i < 6 :
- tempSum += int(hexCode[i], 16) * 16
- tempSum += int(hexCode[i+1], 16)
- rgbValues.append(tempSum)
- i = i + 2
- tempSum = 0
-
- return rgbValues
+# rgbTo() acts as an interface between format handlers and RGB conversions (for easier addition of formats later)
+# ARGS
+# colorFormat: a string containing a color format to convert to
+# rgbValues: the rgbValues to convert
+# RETURNS
+# the converted color values in the specified format
+def rgbTo(colorFormat, rgbValues) :
+ if colorFormat == 'hex' :
+ return RGBtoHEX(rgbValues)
+ if colorFormat == 'cmy' :
+ return RGBtoCMY(rgbValues)
+ if colorFormat == 'cmyk' :
+ return RGBtoCMYK(rgbValues)
+ if colorFormat == 'hsl' :
+ return RGBtoHSVorHSL(rgbValues, 'hsl')
+ if colorFormat == 'hsv' :
+ return RGBtoHSVorHSL(rgbValues, 'hsv')
# Takes in a list of 3 integers, returns a string
def RGBtoHEX(rgbValues) :
@@ -371,6 +351,24 @@ def RGBtoHSVorHSL(rgbValues, convertTo) :
return [hue, saturation * 100, value * 100]
+##
+# OTHER FORMATS -> RGB SECTION
+##
+
+# Takes in a string, returns a list of 3 integers
+def HEXtoRGB(hexCode) :
+ rgbValues = []
+
+ tempSum = 0
+ i = 0
+ while i < 6 :
+ tempSum += int(hexCode[i], 16) * 16
+ tempSum += int(hexCode[i+1], 16)
+ rgbValues.append(tempSum)
+ i = i + 2
+ tempSum = 0
+
+ return rgbValues
# Takes in a list of 3 floats, returns a list of 3 integers
def CMYtoRGB(cmyValues) :
red = (1 - (cmyValues[0] / 100)) * 255
@@ -434,91 +432,15 @@ def HSLorHSVToRGB(values, convertFrom) :
R = chrome + m
G = 0 + m
B = x + m
- return [smartRound(R * 255), smartRound(G * 255), smartRound(B * 255)]
+ return [int(R * 255), int(G * 255), int(B * 255)]
else :
print('RGB to HSV/HSL conversion failed')
return
-##
-# INPUT VALIDATION
-##
-
-def detectColorFormat(color, outputFormats) :
- if '#' in color :
- handleHex(color, outputFormats)
- return True
- elif 'rgb' in color :
- handleRGB(color, outputFormats)
- return True
- elif 'cmyk' in color :
- handleCMYK(color, outputFormats)
- return True
- elif 'cmy' in color :
- handleCMY(color, outputFormats)
- return True
- elif color.strip().startswith('hsl') :
- handleHSVorHSL(color, 'hsl', outputFormats)
- return True
- elif color.strip().startswith('hsv') :
- handleHSVorHSL(color, 'hsv', outputFormats)
- return True
- else :
- return False
-
-
-# Extracts numerical values from a string.
-# Parameters:
-# color: string containing value(s)
-# numValues: determines how many values to extract (e.g. 3 for RGB, 4 for CMYK)
-# isHex: determines if we're looking for hex rather than decimal
-
-# returns: list of extracted color/number values in string form
-def extractValues(color, numValues, isHex = False) :
- i = 0
- tempValue = ''
- extractedValues = []
-
- if isHex :
- # search for hex values
- while i < len(color) :
- if (color[i].isnumeric()) or (color[i] in HEX_LETTERS) :
- tempValue += color[i]
- else :
- tempValue = ''
- if len(tempValue) == 6 :
- extractedValues.append(tempValue)
- tempValue = ''
- if len(extractedValues) == numValues :
- break
- i = i + 1
-
- if (len(extractedValues) != numValues) and (len(tempValue) == 6) :
- extractedValues.append(tempValue)
-
- else :
- # search for decimal values
- while i < len(color) :
- if color[i].isnumeric() or color[i] == '.' :
- tempValue += color[i]
- elif len(tempValue) > 0 and tempValue != '.' :
- extractedValues.append(tempValue)
- tempValue = ''
- else :
- tempValue = ''
- if len(extractedValues) == numValues :
- break
- i = i + 1
-
- if (len(extractedValues) != numValues) and (len(tempValue) > 0) :
- extractedValues.append(tempValue)
-
- if len(extractedValues) != numValues :
- print(f'Could not extract the correct number of values from input: {color}. numValues requested: {numValues}, isHex: {isHex}, Values successfully extracted: {len(extractedValues)}, {extractedValues}')
- return False
-
- return extractedValues
-
+'''
+# INPUT VALIDATION
+'''
# Takes in a string.
# If string contains a valid Hex color code, it gets returned as a string.
@@ -564,7 +486,7 @@ def validateCMYorCMYK(color, include_K) :
for value in values :
value = float(value)
if (value < 0) or (value > 100) :
- print('ERROR: Each CMY(K) value must be between 0,0-100.0(%)')
+ print('ERROR: Each CMY(K) value must be between 0,0-100.0')
return
floatValues.append(value)
@@ -595,14 +517,101 @@ def validateHSLorHSV(color) :
return [color[0], color[1], color[2]]
-##
+'''
# GENERAL UTILITIES
-##
+'''
-# takes in a map of color values and either prints their values to STDOUT or into a specified file
-def printConversions(convertedValues) :
- # format converted values into better output strings
+# ARGS
+# color: a string containing a color value in an unknown format
+# outputFormats: a list containing information necessary for handling conversions, used if format is detected
+# RETURNS
+# truth if success, false if failure to detect a format
+def detectColorFormat(color, outputFormats) :
+ if '#' in color :
+ handleHex(color, outputFormats)
+ return True
+ elif 'rgb' in color :
+ handleRGB(color, outputFormats)
+ return True
+ elif 'cmyk' in color :
+ handleCMYK(color, outputFormats)
+ return True
+ elif 'cmy' in color :
+ handleCMY(color, outputFormats)
+ return True
+ elif color.strip().startswith('hsl') :
+ handleHSVorHSL(color, 'hsl', outputFormats)
+ return True
+ elif color.strip().startswith('hsv') :
+ handleHSVorHSL(color, 'hsv', outputFormats)
+ return True
+ else :
+ return False
+
+
+# Extracts numerical (hex or decimal) values from a string.
+# ARGS
+# color: string containing value(s)
+# numValues: determines how many values to extract (e.g. 3 for RGB, 4 for CMYK)
+# isHex: determines if we're looking for hex rather than decimal
+# RETURNS
+# a list of extracted color/number values in string form (or false if faulure to extract)
+def extractValues(color, numValues, isHex = False) :
+ i = 0
+ tempValue = ''
+ extractedValues = []
+
+ if isHex :
+ # search for hex values
+ while i < len(color) :
+ if (color[i].isnumeric()) or (color[i] in HEX_LETTERS) :
+ tempValue += color[i]
+ else :
+ tempValue = ''
+ if len(tempValue) == 6 :
+ extractedValues.append(tempValue)
+ tempValue = ''
+ if len(extractedValues) == numValues :
+ break
+ i = i + 1
+
+ if (len(extractedValues) != numValues) and (len(tempValue) == 6) :
+ extractedValues.append(tempValue)
+
+ else :
+ # search for decimal values
+ while i < len(color) :
+ if color[i].isnumeric() or color[i] == '.' :
+ tempValue += color[i]
+ elif len(tempValue) > 0 and tempValue != '.' :
+ extractedValues.append(tempValue)
+ tempValue = ''
+ else :
+ tempValue = ''
+ if len(extractedValues) == numValues :
+ break
+ i = i + 1
+
+ if (len(extractedValues) != numValues) and (len(tempValue) > 0) :
+ extractedValues.append(tempValue)
+
+ if len(extractedValues) != numValues :
+ print(f'Could not extract the correct number of values from input: {color}. numValues requested: {numValues}, isHex: {isHex}, Values successfully extracted: {len(extractedValues)}, {extractedValues}')
+ return False
+
+ return extractedValues
+
+
+# formats and delivers color values to STDOUT or a specified output file
+# ARGS
+# convertedValues: a map of the results from the color translations that were performed on a single color value,
+# where the key indicates the format and the value is a list of raw numeric values
+def printConversions(convertedValues) :
output = []
+ if VERBOSE :
+ output.append(convertedValues['verbose-msg'])
+
+ # format converted values into better output strings
for colorFormat in TYPES :
if convertedValues.get(colorFormat, None) is None :
continue
@@ -623,18 +632,22 @@ def printConversions(convertedValues) :
colorCode += str(value) + ', '
output.append(colorCode)
- # determine how to deliver output
+ # deliver formatted output
if OUTPUT != 'stdout' :
- if APPEND :
- with open(OUTPUT, 'a', encoding='utf-8') as file :
- for color in output :
- file.write(color + '\n')
- file.write('\n')
- else :
- with open(OUTPUT, 'w', encoding='utf-8') as file :
- for color in output :
- file.write(color + '\n')
- file.write('\n')
+
+ '''
+ TODO FOR EFFICIENCY:
+
+ right now, unless i'm mistaken and there's some hidden optimization going on, this is
+ going to open and close the file to write the conversions of each inputted color code.
+ if there's a huge file being used as input, this may get slow, so perhaps the best move is
+ to actually open the file back in main() at the point when we evaluate args, and then close
+ it after everything in main() has ran... food for thought.
+ '''
+ with open(OUTPUT, 'a', encoding='utf-8') as file :
+ for color in output :
+ file.write(color + '\n')
+ file.write('\n')
else :
for color in output :
print(color)