about summary refs log tree commit diff
path: root/pkgs/aszlig/aacolorize/aacolorize.py
blob: ff19b6871d1ca8b0992b7106f9af72f5bb3e1358 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python
import os
import sys

from optparse import Option, OptionParser

COLORS = {
    "k": (30, "Black"),
    "r": (31, "Red"),
    "g": (32, "Green"),
    "y": (33, "Yellow"),
    "b": (34, "Blue"),
    "p": (35, "Pink"),
    "c": (36, "Cyan"),
    "w": (37, "White"),
}

ESC = chr(27)

class ColorizeError(Exception):
    pass

class Color(object):
    def __init__(self, ident=None):
        """
        Initialize a color object, if no `ident` is given or it's invalid,
        the Color object represents "no color".
        """
        if ident is not None:
            spec = COLORS.get(ident.lower(), None)
        else:
            spec = None

        if spec is None:
            self.ident = None
            self.bold = False
            self.code = None
            self.name = "None"
        else:
            self.ident = ident
            self.code, self.name = spec

            if ident.isupper():
                self.bold = True
            else:
                self.bold = False

    @property
    def attrs(self):
        """
        A tuple consisting of the SGR attributes.
        """
        if self.ident is None:
            return ()

        if self.bold:
            return (1, self.code)
        else:
            return (self.code,)

    def sgr_attrs(self, *attrs):
        """
        Return the attributes specified by `attrs` formatted according
        to the CSI specification.
        """
        return ';'.join(map(lambda c: str(c), attrs))

    def sgr(self, *attrs):
        """
        Start Set Graphics Rendition
        Return the CSI escape code for `attrs`.
        """
        return "%s[%sm" % (ESC, self.sgr_attrs(*attrs))

    def sgr_start(self):
        """
        Start Set Graphics Rendition
        Return the CSI start escape code for the current color.
        """
        return self.sgr(*self.attrs)

    def sgr_stop(self):
        """
        Clear Set Graphics Rendition
        """
        return self.sgr()

    def apply(self, value):
        """
        Apply the current color to the string in `value`.
        """
        return "%s%s%s" % (self.sgr_start(), value, self.sgr_stop())

    def describe(self):
        """
        Return the description of the current color IN color :-)
        """
        fmt = "%c: <ESC>[%sm -> [%s]"
        return fmt % (
            self.ident,
            self.sgr_attrs(*self.attrs),
            self.apply(self.name)
        )

    def transform_to(self, new_color):
        """
        Return the CSI sequences needed to transform into `new_color`.
        """
        if self.ident is None and new_color.ident is not None:
            return new_color.sgr_start()
        elif self.ident is not None and new_color.ident is None:
            return self.sgr_stop()
        elif self.ident is None and new_color.ident is None:
            return ''
        elif self.code == new_color.code:
            if not self.bold and new_color.bold:
                return self.sgr(1)
            elif self.bold and not new_color.bold:
                return self.sgr(22)
            elif self.bold == new_color.bold:
                return ''
        else:
            if self.bold and new_color.bold:
                return new_color.sgr(new_color.code)

        return self.sgr_stop()+new_color.sgr_start()

    def __repr__(self):
        if self.bold:
            return "<Bold color %s>" % self.name.lower()
        else:
            return "<Color %s>" % self.name.lower()

def print_colortable():
    for ident in COLORS.iterkeys():
        normal = Color(ident).describe()
        bold = Color(ident.upper()).describe()
        sys.stdout.write("%-35s%s\n" % (normal, bold))

def colorize_art(art, colmap):
    if len(art) != len(colmap):
        raise ColorizeError("Art and colormap differ in size!")

    no_color = Color()

    out = ""
    last_color = no_color
    for i, char in enumerate(colmap):
        color = Color(char)
        out += last_color.transform_to(color) + art[i]
        last_color = color

    last_color.transform_to(no_color)

    return out

def colorize_file(artfile, mapfile=None):
    if mapfile is None:
        mapfile = os.path.splitext(artfile)[0]+'.colmap'

    asciiart = open(artfile, 'r').read()
    colormap = open(mapfile, 'r').read()

    return colorize_art(asciiart, colormap)

if __name__ == "__main__":
    parser = OptionParser(usage="%prog [options] artfile [mapfile]")
    parser.add_option("-t", "--table", action="store_true", dest="table",
                      help="Show color table and exit.")

    (options, args) = parser.parse_args()

    if options.table:
        print_colortable()
        parser.exit()

    if not len(args) in (1, 2):
        parser.print_help()
        parser.exit()
    else:
        colorized = colorize_file(*args)
        sys.stdout.write(colorized)