Lumiera  0.pre.03
»edit your freedom«
IconSvgRenderer.py
1 #!/usr/bin/python
2 # coding: utf-8
3 #
4 # IconSvgRenderer.py - Icon rendering utility script
5 #
6 # Copyright (C)
7 # 2008, Joel Holdsworth <joel@airwebreathe.org.uk>
8 #
9 # This program is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by the
11 # Free Software Foundation; either version 2 of the License, or (at your
12 # option) any later version. See the file COPYING for further details.
13 
14 
15 import os
16 import sys
17 import getopt
18 import shutil
19 from xml.dom import minidom
20 
21 
22 rsvgPath = "./rsvg-convert"
23 artworkLayerPrefix = "artwork:"
24 
25 #
26 # 2/2011 some notes by Ichthyo
27 # The purpose of this python script is
28 # - to parse a SVG
29 # - to invoke Inkscape to render this SVG into a raster image (icon)
30 #
31 # For the actual Cairo based SVG rendering we rely on an executable 'rsvg-convert',
32 # which is built during the Lumiera build process.
33 #
34 # Judging from the code and the actual SVGs, this seems to work as follows:
35 # The SVG contains a design to be rendered into raster images of various sizes.
36 # These sizes are determined by special rectangles, which act as bounding box and
37 # are placed on a special 'plate' layer, which is a child layer of the main
38 # 'artwork:' layer. The grid of the SVG is setup such as to result in pixel sizes
39 # suitable for icon generation. The actual size of the generated icons are then
40 # parsed from the height and width attributes of the mentioned bounding box
41 # rectangles.
42 #
43 # The parser seems to be rather simplistic; the sizes and positions need to be
44 # integral numbers. In one instance we had a float number in the y coordinate,
45 # which resulted in an invalid, zero sized output icon
46 #
47 #
48 
49 
50 def createDirectory (name):
51  try:
52  if os.path.isfile (name):
53  os.remove (name)
54  if not os.path.exists (name):
55  os.mkdir (name)
56  except:
57  print 'WARNING: createDirectory("%s") failed. Permission problems?' % name
58 
59 
60 def copyMergeDirectory (src, dst):
61  listing = os.listdir (src)
62  for file_name in listing:
63  src_file_path = os.path.join (src, file_name)
64  dst_file_path = os.path.join (dst, file_name)
65  shutil.copyfile (src_file_path, dst_file_path)
66 
67 def getDocumentSize (svg_element):
68  width = float(svg_element.getAttribute("width"))
69  height = float(svg_element.getAttribute("height"))
70  return [width, height]
71 
72 def findChildLayerElement (parent_element):
73  for node in parent_element.childNodes:
74  if node.nodeType == minidom.Node.ELEMENT_NODE:
75  if node.tagName == "g":
76  if node.getAttribute("inkscape:groupmode") == "layer":
77  return node
78  return None
79 
80 def parsePlateLayer (layer):
81  rectangles = []
82  for node in layer.childNodes:
83  if node.nodeType == minidom.Node.ELEMENT_NODE:
84  if node.tagName == "rect":
85  x = float(node.getAttribute("x"))
86  y = float(node.getAttribute("y"))
87  width = float(node.getAttribute("width"))
88  height = float(node.getAttribute("height"))
89  rectangles.append([x, y, width, height])
90  return rectangles
91 
92 
93 def parseSVG (file_path):
94  print "Parsing " + file_path
95  svgdoc = minidom.parse (file_path)
96  for root_node in svgdoc.childNodes:
97  if root_node.nodeType == minidom.Node.ELEMENT_NODE:
98  if root_node.tagName == "svg":
99  size = getDocumentSize (root_node)
100  layer = findChildLayerElement (root_node)
101  if layer != None:
102  layer_name = layer.getAttribute ("inkscape:label")
103  if layer_name[:len(artworkLayerPrefix)] == artworkLayerPrefix:
104  artwork_name = layer_name[len(artworkLayerPrefix):]
105  plate = findChildLayerElement(layer)
106  if plate != None:
107  return artwork_name, size, parsePlateLayer(plate)
108  return None
109 
110 
111 def renderSvgRsvg (file_path, out_dir, artwork_name, rectangle, _doc_size):
112  # Prepare a Cairo context
113  width = int(rectangle[2])
114  height = int(rectangle[3])
115 
116  if not os.path.exists(rsvgPath):
117  print "Error: executable %s not found." % rsvgPath
118 
119  os.spawnlp(os.P_WAIT, rsvgPath, rsvgPath,
120  "--source-rect=%g:%g:%g:%g" % (rectangle[0], rectangle[1], width, height),
121  "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (width, height, artwork_name)),
122  file_path)
123 
124 def renderSvgIcon (file_path, out_dir):
125  artwork_name, doc_size, rectangles = parseSVG (file_path)
126  for rectangle in rectangles:
127  renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size)
128 
129 def getTargetNames (file_path):
130  """get a list of target names to be rendered from the given source SVG
131  usable to setup the build targets for SCons
132  """
133  artwork_name, _ , rectangles = parseSVG (file_path)
134  return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ]
135 
136 
137 def printHelp():
138  print "render-icon.py SRCFILE.svg TARGETDIR"
139  print "An icon rendering utility script for lumiera"
140 
141 def parseArguments(argv):
142  _optlist, args = getopt.getopt(argv, "")
143 
144  if len(args) == 2:
145  return args[0], args[1]
146 
147  printHelp()
148  return None, None
149 
150 
151 def main (argv):
152  in_path, out_dir = parseArguments(argv)
153 
154  if not (in_path and out_dir):
155  print "Missing arguments in_path and out_dir."
156  sys.exit(1)
157 
158  if os.path.isfile(out_dir):
159  print "Unable to use '%s' as output directory, because it\'s a file." % out_dir
160  sys.exit(1)
161  if not os.path.isdir(out_dir):
162  print "Output directory '%s' not found." % out_dir
163  sys.exit(1)
164 
165  # Create the icons folders
166  createDirectory(os.path.join(out_dir, "48x48"))
167  createDirectory(os.path.join(out_dir, "32x32"))
168  createDirectory(os.path.join(out_dir, "24x24"))
169  createDirectory(os.path.join(out_dir, "22x22"))
170  createDirectory(os.path.join(out_dir, "16x16"))
171 
172  renderSvgIcon (in_path, out_dir)
173 
174 
175 
176 
177 if __name__=="__main__":
178  main(sys.argv[1:])
179 
int main(int argc, const char *argv[])
run all tests or any single test specified in the first command line argument.
Definition: testrunner.cpp:37