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