--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script extracts pngs from a single svg for all objects with ids
+# beginning by 0. Requires Inkscape.
+module svg_to_png_and_nit
+
+import opts
+import template
+
+class Image
+ var name: String
+ var x: Int
+ var y: Int
+ var w: Int
+ var h: Int
+ fun right: Int do return x+w
+ fun bottom: Int do return y+h
+
+ redef fun to_s do return name
+end
+
+# The Nit source file to retreive all images
+class ImageSetSrc
+ super Template
+
+ var name: String
+ init(name: String) do self.name = name
+
+ var attributes = new Array[String]
+ var load_exprs = new Array[String]
+
+ redef fun rendering
+ do
+ add """
+# file generated by svg_to_png, do not modify, redef instead
+
+import generated_image_set
+
+class {{{name}}}
+ super ImageSet
+
+"""
+ add_all attributes
+ add """
+
+ redef fun load_all(app: App)
+ do
+"""
+ add_all load_exprs
+ add """
+ end
+end
+"""
+ end
+end
+
+redef class String
+ fun to_i_strip_e: Int
+ do
+ if has_substring("e-", 0) then return 0
+ return to_i
+ end
+end
+
+redef class Int
+ fun adapt(d: Int, scale: Float): Int
+ do
+ var corrected = self-d
+ return (corrected.to_f*scale).to_i
+ end
+
+ fun next_pow2: Int
+ do
+ var p = 2
+ while p < self do p = p*2
+ return p
+ end
+end
+
+var opt_out_src = new OptionString("Path to output source file", "--src", "-s")
+var opt_assets = new OptionString("Path to assert dir where to put PNG files", "--assets", "-a")
+var opt_scale = new OptionFloat("Apply scaling to exported images (defaut at 1.0 of 90dpi)", 1.0, "--scale", "-x")
+var opt_help = new OptionBool("Print this help message", "--help", "-h")
+
+var opt_context = new OptionContext
+opt_context.add_option(opt_out_src, opt_assets, opt_scale, opt_help)
+
+opt_context.parse(args)
+var rest = opt_context.rest
+var errors = opt_context.errors
+if rest.is_empty and not opt_help.value then errors.add "You must specify at least one source drawing file"
+if not errors.is_empty or opt_help.value then
+ print errors.join("\n")
+ print "Usage: svg_to_png_and_nit [Options] drawing.svg [Other files]"
+ print "Options:"
+ opt_context.usage
+ exit 1
+end
+
+var drawings = rest
+for drawing in drawings do
+ if not drawing.file_exists then
+ stderr.write "Source drawing file '{drawing}' does not exist."
+ exit 1
+ end
+end
+
+var assets_path = opt_assets.value
+if assets_path == null then assets_path = "assets"
+if not assets_path.file_exists then
+ stderr.write "Assets dir '{assets_path}' does not exist (use --assets)\n"
+ exit 1
+end
+
+var src_path = opt_out_src.value
+if src_path == null then src_path = "src"
+if not src_path.file_exists then
+ stderr.write "Source dir '{src_path}' does not exist (use --src)\n"
+ exit 1
+end
+
+var scale = opt_scale.value
+
+var arrays_of_images = new Array[String]
+
+for drawing in drawings do
+ var drawing_name = drawing.basename(".svg")
+
+ # Get the page dimensions
+ # Inkscape doesn't give us this information
+ var page_width = -1
+ var page_height = -1
+ var svg_file = new IFStream.open(drawing)
+ while not svg_file.eof do
+ var line = svg_file.read_line
+
+ if page_width == -1 and line.search("width") != null then
+ var words = line.split("=")
+ var n = words[1]
+ n = n.substring(1, n.length-2) # remove ""
+ page_width = n.to_i_strip_e
+ else if page_height == -1 and line.search("height") != null then
+ var words = line.split("=")
+ var n = words[1]
+ n = n.substring(1, n.length-2) # remove ""
+ page_height = n.to_i_strip_e
+ end
+ end
+ svg_file.close
+
+ assert page_width != -1
+ assert page_height != -1
+
+ # Query Inkscape
+ var prog = "inkscape"
+ var proc = new IProcess.from_a(prog, ["--without-gui", "--query-all", drawing])
+
+ var min_x = 1000000
+ var min_y = 1000000
+ var max_x = -1
+ var max_y = -1
+ var images = new Array[Image]
+
+ # Gather all images beginning with 0
+ # also get the bounding box of all images
+ while not proc.eof do
+ var line = proc.read_line
+ var words = line.split(",")
+
+ if words.length == 5 then
+ var id = words[0]
+
+ var x = words[1].to_i_strip_e
+ var y = words[2].to_i_strip_e
+ var w = words[3].to_i_strip_e
+ var h = words[4].to_i_strip_e
+
+ if id.has_prefix("0") then
+ var nit_name = id.substring_from(1)
+ nit_name = nit_name.replace('-', "_")
+
+ var image = new Image(nit_name, x, y, w, h)
+ min_x = min_x.min(x)
+ min_y = min_y.min(y)
+ max_x = max_x.max(image.right)
+ max_y = max_y.max(image.bottom)
+
+ images.add image
+ end
+ end
+ end
+ proc.close
+
+ # Nit class
+ var nit_class_name = drawing_name.chars.first.to_s.to_upper + drawing_name.substring_from(1) + "Images"
+ var nit_src = new ImageSetSrc(nit_class_name)
+ nit_src.attributes.add "\tprivate var main_image: Image\n"
+ nit_src.load_exprs.add "\t\tmain_image = app.load_image(\"images/{drawing_name}.png\")\n"
+
+ # Sort images by name, it prevents Array errors and looks better
+ alpha_comparator.sort(images)
+
+ # Add images to Nit source file
+ for image in images do
+ # Adapt coordinates to new top left and scale
+ var x = image.x.adapt(min_x, scale)
+ var y = image.y.adapt(min_y, scale)
+ var w = (image.w.to_f*scale).to_i
+ var h = (image.h.to_f*scale).to_i
+
+ var nit_name = image.name
+ var last_char = nit_name.chars.last
+ if last_char.to_s.is_numeric then
+ # Array of images
+ # TODO support more than 10 images in an array
+
+ nit_name = nit_name.substring(0, nit_name.length-1)
+ if not arrays_of_images.has(nit_name) then
+ # Create class attribute to store Array
+ arrays_of_images.add(nit_name)
+ nit_src.attributes.add "\tvar {nit_name} = new Array[Image]\n"
+ end
+ nit_src.load_exprs.add "\t\t{nit_name}.add(main_image.subimage({x}, {y}, {w}, {h}))\n"
+ else
+ # Single image
+ nit_src.attributes.add "\tvar {nit_name}: Image\n"
+ nit_src.load_exprs.add "\t\t{nit_name} = main_image.subimage({x}, {y}, {w}, {h})\n"
+ end
+ end
+
+ # Output source file
+ var src_file = new OFStream.open("{src_path}/{drawing_name}.nit")
+ nit_src.write_to(src_file)
+ src_file.close
+
+ # Find closest power of 2
+ var dx = max_x - min_x
+ max_x = dx.next_pow2 + min_x
+
+ var dy = max_y - min_y
+ max_y = dy.next_pow2 + min_y
+
+ # Inkscape's --export-area inverts the Y axis. It uses the lower left corner of
+ # the drawing area where as queries return coordinates from the top left.
+ var y0 = page_height - max_y
+ var y1 = page_height - min_y
+
+ # Output png file to assets
+ var png_path = "{assets_path}/images/{drawing_name}.png"
+ var proc2 = new Process.from_a(prog, [drawing, "--without-gui",
+ "--export-dpi={(90.0*scale).to_i}",
+ "--export-png={png_path}",
+ "--export-area={min_x}:{y0}:{max_x}:{y1}",
+ "--export-background=#000000", "--export-background-opacity=0.0"])
+ proc2.wait
+end
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="drawing.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.7"
+ inkscape:cx="121.41831"
+ inkscape:cy="436.77983"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1962"
+ inkscape:window-height="1240"
+ inkscape:window-x="2718"
+ inkscape:window-y="1285"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="fill:#0caf0c;fill-opacity:1;stroke:#3b3b3b;stroke-width:1.58335221;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="0square"
+ width="98.416649"
+ height="98.416649"
+ x="83.827385"
+ y="181.54672"
+ inkscape:label="#rect2985" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ff7900;fill-opacity:1;stroke:#3b3b3b;stroke-width:2.50000000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="0circle"
+ sodipodi:cx="556.42859"
+ sodipodi:cy="290.21933"
+ sodipodi:rx="85"
+ sodipodi:ry="85"
+ d="m 641.42859,290.21933 a 85,85 0 1 1 -170,0 85,85 0 1 1 170,0 z"
+ transform="translate(-124.28571,-85.714286)"
+ inkscape:label="#path2987" />
+ <path
+ transform="translate(-405.71428,218.57143)"
+ d="m 641.42859,290.21933 a 85,85 0 1 1 -170,0 85,85 0 1 1 170,0 z"
+ sodipodi:ry="85"
+ sodipodi:rx="85"
+ sodipodi:cy="290.21933"
+ sodipodi:cx="556.42859"
+ id="0circles0"
+ style="fill:#0095ff;fill-opacity:1;stroke:#3b3b3b;stroke-width:2.50000000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ sodipodi:type="arc"
+ inkscape:label="#path2989" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#0095ff;fill-opacity:1;stroke:#3b3b3b;stroke-width:3.33612041999999986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.71604937999999996"
+ id="0circles1"
+ sodipodi:cx="556.42859"
+ sodipodi:cy="290.21933"
+ sodipodi:rx="85"
+ sodipodi:ry="85"
+ d="m 641.42859,290.21933 a 85,85 0 1 1 -170,0 85,85 0 1 1 170,0 z"
+ transform="matrix(0.74937343,0,0,0.74937343,-104.82992,291.3081)"
+ inkscape:label="#path2991" />
+ <path
+ transform="matrix(0.39849624,0,0,0.39849624,214.6939,393.13944)"
+ d="m 641.42859,290.21933 a 85,85 0 1 1 -170,0 85,85 0 1 1 170,0 z"
+ sodipodi:ry="85"
+ sodipodi:rx="85"
+ sodipodi:cy="290.21933"
+ sodipodi:cx="556.42859"
+ id="0circles2"
+ style="fill:#0095ff;fill-opacity:1;stroke:#3b3b3b;stroke-width:6.27358492000000023;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;opacity:0.28806584000000002"
+ sodipodi:type="arc"
+ inkscape:label="#path2993" />
+ <g
+ id="g3767">
+ <text
+ sodipodi:linespacing="125%"
+ id="text3763"
+ y="912.59924"
+ x="83.155045"
+ style="font-size:54.63201141px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ y="912.59924"
+ x="83.155045"
+ id="tspan3765"
+ sodipodi:role="line">Not to extract</tspan></text>
+ </g>
+ </g>
+</svg>