1#!/usr/bin/python3
2
3# Thanks to the GNOME theme nerds for the original source of this script
4
5import os
6import sys
7import xml.sax
8import subprocess
9
10INKSCAPE = '/usr/bin/inkscape'
11OPTIPNG = '/usr/bin/optipng'
12MAINDIR = '../'
13SRC = os.path.join('.', 'wm')
14
15inkscape_process = None
16
17
18def optimize_png(png_file):
19    if os.path.exists(OPTIPNG):
20        process = subprocess.Popen([OPTIPNG, '-quiet', '-o7', png_file])
21        process.wait()
22
23
24def wait_for_prompt(process, command=None):
25    if command is not None:
26        process.stdin.write((command+'\n').encode('utf-8'))
27
28    # This is kinda ugly ...
29    # Wait for just a '>', or '\n>' if some other char appearead first
30    output = process.stdout.read(1)
31    if output == b'>':
32        return
33
34    output += process.stdout.read(1)
35    while output != b'\n>':
36        output += process.stdout.read(1)
37        output = output[1:]
38
39
40def start_inkscape():
41    process = subprocess.Popen(
42        [INKSCAPE, '--shell'],
43        bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE
44        )
45    wait_for_prompt(process)
46    return process
47
48
49def inkscape_render_rect(icon_file, rect, output_file):
50    global inkscape_process
51    if inkscape_process is None:
52        inkscape_process = start_inkscape()
53    wait_for_prompt(inkscape_process,
54                    '--export-dpi=180 %s -i %s -e %s' % (icon_file, rect, output_file)
55                    )
56    optimize_png(output_file)
57
58
59class ContentHandler(xml.sax.ContentHandler):
60    ROOT = 0
61    SVG = 1
62    LAYER = 2
63    OTHER = 3
64    TEXT = 4
65
66    def __init__(self, path, force=False, filter=None):
67        self.stack = [self.ROOT]
68        self.inside = [self.ROOT]
69        self.path = path
70        self.rects = []
71        self.state = self.ROOT
72        self.chars = ""
73        self.force = force
74        self.filter = filter
75
76    def endDocument(self):
77        pass
78
79    def startElement(self, name, attrs):
80        if self.inside[-1] == self.ROOT:
81            if name == "svg":
82                self.stack.append(self.SVG)
83                self.inside.append(self.SVG)
84                return
85        elif self.inside[-1] == self.SVG:
86            if (name == "g" and ('inkscape:groupmode' in attrs) and ('inkscape:label' in attrs)
87               and attrs['inkscape:groupmode'] == 'layer' and attrs['inkscape:label'].startswith('Baseplate')):
88                self.stack.append(self.LAYER)
89                self.inside.append(self.LAYER)
90                self.context = None
91                self.icon_name = None
92                self.rects = []
93                return
94        elif self.inside[-1] == self.LAYER:
95            if name == "text" and ('inkscape:label' in attrs) and attrs['inkscape:label'] == 'context':
96                self.stack.append(self.TEXT)
97                self.inside.append(self.TEXT)
98                self.text='context'
99                self.chars = ""
100                return
101            elif name == "text" and ('inkscape:label' in attrs) and attrs['inkscape:label'] == 'icon-name':
102                self.stack.append(self.TEXT)
103                self.inside.append(self.TEXT)
104                self.text = 'icon-name'
105                self.chars = ""
106                return
107            elif name == "rect":
108                self.rects.append(attrs)
109
110        self.stack.append(self.OTHER)
111
112    def endElement(self, name):
113        stacked = self.stack.pop()
114        if self.inside[-1] == stacked:
115            self.inside.pop()
116
117        if stacked == self.TEXT and self.text is not None:
118            assert self.text in ['context', 'icon-name']
119            if self.text == 'context':
120                self.context = self.chars
121            elif self.text == 'icon-name':
122                self.icon_name = self.chars
123            self.text = None
124        elif stacked == self.LAYER:
125            assert self.icon_name
126            assert self.context
127
128            if self.filter is not None and not self.icon_name in self.filter:
129                return
130
131            print (self.context, self.icon_name)
132            for rect in self.rects:
133                width = rect['width']
134                height = rect['height']
135                id = rect['id']
136
137                dir = os.path.join(MAINDIR, self.context)
138                outfile = os.path.join(dir, self.icon_name+'.png')
139                if not os.path.exists(dir):
140                    os.makedirs(dir)
141                # Do a time based check!
142                if self.force or not os.path.exists(outfile):
143                    inkscape_render_rect(self.path, id, outfile)
144                    sys.stdout.write('.')
145                else:
146                    stat_in = os.stat(self.path)
147                    stat_out = os.stat(outfile)
148                    if stat_in.st_mtime > stat_out.st_mtime:
149                        inkscape_render_rect(self.path, id, outfile)
150                        sys.stdout.write('.')
151                    else:
152                        sys.stdout.write('-')
153                sys.stdout.flush()
154            sys.stdout.write('\n')
155            sys.stdout.flush()
156
157    def characters(self, chars):
158        self.chars += chars.strip()
159
160if len(sys.argv) == 1:
161    if not os.path.exists(MAINDIR):
162        os.mkdir(MAINDIR)
163    print ('Rendering from SVGs in', SRC)
164    for file in os.listdir(SRC):
165        if file[-4:] == '.svg':
166            file = os.path.join(SRC, file)
167            handler = ContentHandler(file)
168            xml.sax.parse(open(file), handler)
169else:
170    file = os.path.join(SRC, sys.argv[1] + '.svg')
171    if len(sys.argv) > 2:
172        icons = sys.argv[2:]
173    else:
174        icons = None
175    if os.path.exists(os.path.join(file)):
176        handler = ContentHandler(file, True, filter=icons)
177        xml.sax.parse(open(file), handler)
178    else:
179        print ("Error: No such file", file)
180        sys.exit(1)
181