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