Created
February 11, 2012 07:59
-
-
Save Klowner/1797743 to your computer and use it in GitHub Desktop.
Color template engine, useful for building collections of CSS files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import re | |
import colorsys | |
class ColorFunc(object): | |
MATCH = re.compile('^([a-z]+)\(([^\)]+)') | |
def __init__(self, value): | |
self.func_name, self.func_args = self.MATCH.match(value).groups() | |
self.func_args = [x.strip() for x in filter(lambda x:x, self.func_args.split(','))] | |
def resolve(self, parser, prop): | |
funcname = "func_%s" % self.func_name | |
func = getattr(self, funcname, None) | |
if callable(func): | |
return func(prop) | |
else: | |
raise ValueError | |
def func_brightness(self, prop): | |
adjust = int(self.func_args[0])/255.0 | |
h,l,s = colorsys.rgb_to_hls(*prop) | |
l = min(max(l+adjust, 0.0), 1.0) | |
return colorsys.hls_to_rgb(h,l,s) | |
class ColorName(object): | |
MATCH = re.compile('^([A-Z0-9_]+)') | |
def __init__(self, value): | |
self.value = value | |
def resolve(self, parser, prop): | |
if prop: | |
raise ValueError, 'ColorName does not accept operational values' | |
if self.value not in parser.colors: | |
raise ValueError, 'ColorName: %s not defined' % self.value | |
return parser.get(self.value, as_tuple=True) | |
class ColorHex(object): | |
MATCH = re.compile('^#([A-Fa-f|\d]{6})') | |
def __init__(self, value): | |
value = self.MATCH.match(value).groups()[0] | |
self.value = ( | |
int(value[0:2], 16) / 255.0, # Red | |
int(value[2:4], 16) / 255.0, # Blue | |
int(value[4:6], 16) / 255.0, # Green | |
) | |
def resolve(self, parser, prop): | |
if prop: | |
raise ValueError, 'ColorHex does not accept operational values' | |
return self.value | |
class ColorsParser(object): | |
STATEMENT_TYPES = [ | |
ColorFunc, | |
ColorName, | |
ColorHex, | |
] | |
def __init__(self, src=None): | |
self.colors = {} | |
if src: | |
for row in src: | |
self.add_item(row) | |
def add_item(self, row): | |
tokens = self._tokenize(row) | |
if tokens: | |
statements = self.parse_statements(tokens[1]) | |
name = tokens[0] | |
else: | |
statements = None | |
if statements: | |
self.colors[name] = statements | |
def _tokenize(self, row): | |
if ':' in row: | |
name, statement = row.split(':')[:2] | |
statements = filter(lambda x:x, statement.split(' ')) | |
return (name, statements) | |
return None | |
def parse_statements(self, statements): | |
results = [] | |
for stmt in statements: | |
results.append( self._statement_get_type(stmt) ) | |
return results | |
def _statement_get_type(self, statement): | |
for SType in self.STATEMENT_TYPES: | |
match = SType.MATCH.match(statement) | |
if match: | |
return SType(statement) | |
return None | |
def resolve(self, statements): | |
left_prop = None | |
for prop in statements: | |
left_prop = prop.resolve(self, left_prop) | |
return left_prop | |
def to_hex(self, rgb): | |
result = '#' | |
for x in rgb: | |
result += ("%02x" % int(round(x*255))) | |
return result | |
def get(self, name, as_tuple=False): | |
result = self.resolve(self.colors[name]) | |
if as_tuple: | |
return result | |
return self.to_hex(result) | |
class TemplateProcessor(object): | |
def __init__(self, color_parser): | |
self.color_parser = color_parser | |
def process(self, source): | |
tokens = self._tokenize(source) | |
cp = self.color_parser | |
out = [] | |
for (is_statement, token) in tokens: | |
if is_statement: | |
stmt_result = cp.to_hex(cp.resolve(cp.parse_statements( token ))) | |
out.append( stmt_result) | |
else: | |
out.append( token ) | |
return out | |
def _tokenize(self, istream): | |
import StringIO | |
results = [] | |
obuff = StringIO.StringIO() | |
mode = 0 | |
while True: | |
c = istream.read(1) | |
if mode == 0: | |
if c == '$': | |
if istream.read(1) == '{': | |
obuff.seek(0) | |
results.append((mode, obuff.read())) | |
obuff.truncate(0) | |
#istream.seek(istream.tell()-2) | |
mode = 1 | |
else: | |
istream.seek(istream.tell()-1) | |
obuff.write(c) | |
else: | |
obuff.write(c) | |
else: | |
if c == '}': | |
obuff.seek(0) | |
results.append((mode, obuff.read().split(' '))) | |
obuff.truncate(0) | |
mode = 0 | |
else: | |
obuff.write(c) | |
if not c: | |
break | |
obuff.seek(0) | |
results.append((mode, obuff.read())) | |
return results | |
if __name__ == '__main__': | |
import argparse | |
import sys | |
parser = argparse.ArgumentParser(description='Process color definition file(s) and spew out a useful result.') | |
parser.add_argument('colordefs', nargs='+', type=argparse.FileType('r')) | |
parser.add_argument('template', nargs=1, type=argparse.FileType('r')) | |
parser.add_argument('-o', dest='output', nargs='?', type=argparse.FileType('w'), default=sys.stdout) | |
args = parser.parse_args() | |
print args | |
color_parser = ColorsParser() | |
for colordef in args.colordefs: | |
[color_parser.add_item(x) for x in colordef.readlines()] | |
template = TemplateProcessor(color_parser) | |
results = template.process(args.template[0]) | |
args.output.write(''.join(results)) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
div.beautiful { | |
border: #ff0000; | |
background: #e10000; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RED: #ff0000 | |
GREEN: #00ff00 | |
BLUE: #0000ff |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
div.beautiful { | |
border: ${RED}; | |
background: ${RED brightness(-20) brightness(+5)}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment