render-gtk3-assets-hidpi.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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'], bufsize=0,
  31. 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. '--export-dpi=180 %s -i %s -e %s'
  41. % (icon_file, rect, output_file)
  42. )
  43. optimize_png(output_file)
  44. class ContentHandler(xml.sax.ContentHandler):
  45. ROOT = 0
  46. SVG = 1
  47. LAYER = 2
  48. OTHER = 3
  49. TEXT = 4
  50. def __init__(self, path, force=False, filter=None):
  51. self.stack = [self.ROOT]
  52. self.inside = [self.ROOT]
  53. self.path = path
  54. self.rects = []
  55. self.state = self.ROOT
  56. self.chars = ""
  57. self.force = force
  58. self.filter = filter
  59. def endDocument(self):
  60. pass
  61. def startElement(self, name, attrs):
  62. if self.inside[-1] == self.ROOT:
  63. if name == "svg":
  64. self.stack.append(self.SVG)
  65. self.inside.append(self.SVG)
  66. return
  67. elif self.inside[-1] == self.SVG:
  68. if (name == "g" and ('inkscape:groupmode' in attrs) and ('inkscape:label' in attrs)
  69. and attrs['inkscape:groupmode'] == 'layer' and attrs['inkscape:label'].startswith('Baseplate')):
  70. self.stack.append(self.LAYER)
  71. self.inside.append(self.LAYER)
  72. self.context = None
  73. self.icon_name = None
  74. self.rects = []
  75. return
  76. elif self.inside[-1] == self.LAYER:
  77. if name == "text" and ('inkscape:label' in attrs) and attrs['inkscape:label'] == 'context':
  78. self.stack.append(self.TEXT)
  79. self.inside.append(self.TEXT)
  80. self.text = 'context'
  81. self.chars = ""
  82. return
  83. elif name == "text" and ('inkscape:label' in attrs) and attrs['inkscape:label'] == 'icon-name':
  84. self.stack.append(self.TEXT)
  85. self.inside.append(self.TEXT)
  86. self.text = 'icon-name'
  87. self.chars = ""
  88. return
  89. elif name == "rect":
  90. self.rects.append(attrs)
  91. self.stack.append(self.OTHER)
  92. def endElement(self, name):
  93. stacked = self.stack.pop()
  94. if self.inside[-1] == stacked:
  95. self.inside.pop()
  96. if stacked == self.TEXT and self.text is not None:
  97. assert self.text in ['context', 'icon-name']
  98. if self.text == 'context':
  99. self.context = self.chars
  100. elif self.text == 'icon-name':
  101. self.icon_name = self.chars
  102. self.text = None
  103. elif stacked == self.LAYER:
  104. assert self.icon_name
  105. assert self.context
  106. if self.filter is not None and not self.icon_name in self.filter:
  107. return
  108. print (self.context, self.icon_name)
  109. for rect in self.rects:
  110. width = rect['width']
  111. height = rect['height']
  112. id = rect['id']
  113. dir = os.path.join(MAINDIR, self.context)
  114. outfile = os.path.join(dir, self.icon_name+'@2'+'.png')
  115. if not os.path.exists(dir):
  116. os.makedirs(dir)
  117. # Do a time based check!
  118. if self.force or not os.path.exists(outfile):
  119. inkscape_render_rect(self.path, id, outfile)
  120. sys.stdout.write('.')
  121. else:
  122. stat_in = os.stat(self.path)
  123. stat_out = os.stat(outfile)
  124. if stat_in.st_mtime > stat_out.st_mtime:
  125. inkscape_render_rect(self.path, id, outfile)
  126. sys.stdout.write('.')
  127. else:
  128. sys.stdout.write('-')
  129. sys.stdout.flush()
  130. sys.stdout.write('\n')
  131. sys.stdout.flush()
  132. def characters(self, chars):
  133. self.chars += chars.strip()
  134. if len(sys.argv) == 1:
  135. if not os.path.exists(MAINDIR):
  136. os.mkdir(MAINDIR)
  137. print ('Rendering from SVGs in', SRC)
  138. for file in os.listdir(SRC):
  139. if file[-4:] == '.svg':
  140. file = os.path.join(SRC, file)
  141. handler = ContentHandler(file)
  142. xml.sax.parse(open(file), handler)
  143. else:
  144. file = os.path.join(SRC, sys.argv[1] + '.svg')
  145. if len(sys.argv) > 2:
  146. icons = sys.argv[2:]
  147. else:
  148. icons = None
  149. if os.path.exists(os.path.join(file)):
  150. handler = ContentHandler(file, True, filter=icons)
  151. xml.sax.parse(open(file), handler)
  152. else:
  153. print ("Error: No such file", file)
  154. sys.exit(1)