render-gtk3-assets.py 5.6 KB

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