f718255574b695ee2bea7bd2c51778948ace0ad3
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # This script extracts pngs from a single svg for all objects with ids
18 # beginning by 0. Requires Inkscape.
19 module svg_to_png_and_nit
30 fun right
: Int do return x
+w
31 fun bottom
: Int do return y
+h
33 redef fun to_s
do return name
36 # The Nit source file to retreive all images
42 var attributes
= new Array[String]
43 var load_exprs
= new Array[String]
48 # file generated by svg_to_png, do not modify, redef instead
50 import mnit::image_set
59 redef fun load_all(app: App)
71 fun adapt
(d
: Int, scale
: Float): Int
73 var corrected
= self-d
74 return (corrected
.to_f
*scale
).to_i
80 while p
< self do p
= p
*2
85 var opt_out_src
= new OptionString("Path to output source file", "--src", "-s")
86 var opt_assets
= new OptionString("Path to assert dir where to put PNG files", "--assets", "-a")
87 var opt_scale
= new OptionFloat("Apply scaling to exported images (defaut at 1.0 of 90dpi)", 1.0, "--scale", "-x")
88 var opt_help
= new OptionBool("Print this help message", "--help", "-h")
90 var opt_context
= new OptionContext
91 opt_context
.add_option
(opt_out_src
, opt_assets
, opt_scale
, opt_help
)
93 opt_context
.parse
(args
)
94 var rest
= opt_context
.rest
95 var errors
= opt_context
.errors
96 if rest
.is_empty
and not opt_help
.value
then errors
.add
"You must specify at least one source drawing file"
97 if not errors
.is_empty
or opt_help
.value
then
98 print errors
.join
("\n")
99 print
"Usage: svg_to_png_and_nit [Options] drawing.svg [Other files]"
105 if not "inkscape".program_is_in_path
then
106 print
"This tool needs the external program `inkscape`, make sure it is installed and in your PATH."
111 for drawing
in drawings
do
112 if not drawing
.file_exists
then
113 stderr
.write
"Source drawing file '{drawing}' does not exist."
118 var assets_path
= opt_assets
.value
119 if assets_path
== null then assets_path
= "assets"
120 if not assets_path
.file_exists
then
121 stderr
.write
"Assets dir '{assets_path}' does not exist (use --assets)\n"
125 var src_path
= opt_out_src
.value
126 if src_path
== null then src_path
= "src"
127 if not src_path
.file_exists
then
128 stderr
.write
"Source dir '{src_path}' does not exist (use --src)\n"
132 var scale
= opt_scale
.value
134 var arrays_of_images
= new Array[String]
136 for drawing
in drawings
do
137 var drawing_name
= drawing
.basename
(".svg")
139 # Get the page dimensions
140 # Inkscape doesn't give us this information
143 var svg_file
= new FileReader.open
(drawing
)
144 while not svg_file
.eof
do
145 var line
= svg_file
.read_line
147 if page_width
== -1 and line
.search
("width") != null then
148 var words
= line
.split
("=")
150 n
= n
.substring
(1, n
.length-2
) # remove ""
151 page_width
= n
.to_f
.ceil
.to_i
152 else if page_height
== -1 and line
.search
("height") != null then
153 var words
= line
.split
("=")
155 n
= n
.substring
(1, n
.length-2
) # remove ""
156 page_height
= n
.to_f
.ceil
.to_i
161 assert page_width
!= -1
162 assert page_height
!= -1
165 var prog
= "inkscape"
166 var proc
= new ProcessReader.from_a
(prog
, ["--without-gui", "--query-all", drawing
])
172 var images
= new Array[Image]
174 # Gather all images beginning with 0
175 # also get the bounding box of all images
176 while not proc
.eof
do
177 var line
= proc
.read_line
178 var words
= line
.split
(",")
180 if words
.length
== 5 then
183 var x
= words
[1].to_f
.floor
.to_i
184 var y
= words
[2].to_f
.floor
.to_i
185 var w
= words
[3].to_f
.ceil
.to_i
+1
186 var h
= words
[4].to_f
.ceil
.to_i
+1
188 if id
.has_prefix
("0") then
189 var nit_name
= id
.substring_from
(1)
190 nit_name
= nit_name
.replace
('-', "_")
192 var image
= new Image(nit_name
, x
, y
, w
, h
)
195 max_x
= max_x
.max
(image
.right
)
196 max_y
= max_y
.max
(image
.bottom
)
205 var nit_class_name
= drawing_name
.chars
.first
.to_s
.to_upper
+ drawing_name
.substring_from
(1) + "Images"
206 var nit_src
= new ImageSetSrc(nit_class_name
)
207 nit_src
.attributes
.add
"\tprivate var main_image: Image is noinit\n"
208 nit_src
.load_exprs
.add
"\t\tmain_image = app.load_image(\"images
/{drawing_name}.png\
")\n"
210 # Sort images by name, it prevents Array errors and looks better
211 alpha_comparator
.sort
(images
)
213 # Add images to Nit source file
214 for image
in images
do
215 # Adapt coordinates to new top left and scale
216 var x
= image
.x
.adapt
(min_x
, scale
)
217 var y
= image
.y
.adapt
(min_y
, scale
)
218 var w
= (image
.w
.to_f
*scale
).to_i
219 var h
= (image
.h
.to_f
*scale
).to_i
221 var nit_name
= image
.name
222 var last_char
= nit_name
.chars
.last
223 if last_char
.to_s
.is_numeric
then
225 # TODO support more than 10 images in an array
227 nit_name
= nit_name
.substring
(0, nit_name
.length-1
)
228 if not arrays_of_images
.has
(nit_name
) then
229 # Create class attribute to store Array
230 arrays_of_images
.add
(nit_name
)
231 nit_src
.attributes
.add
"\tvar {nit_name} = new Array[Image]\n"
233 nit_src
.load_exprs
.add
"\t\t{nit_name}.add(main_image.subimage({x}, {y}, {w}, {h}))\n"
236 nit_src
.attributes
.add
"\tvar {nit_name}: Image is noinit\n"
237 nit_src
.load_exprs
.add
"\t\t{nit_name} = main_image.subimage({x}, {y}, {w}, {h})\n"
242 var src_file
= new FileWriter.open
("{src_path}/{drawing_name}.nit")
243 nit_src
.write_to
(src_file
)
246 # Find closest power of 2
247 var dx
= max_x
- min_x
248 max_x
= dx
.next_pow2
+ min_x
250 var dy
= max_y
- min_y
251 max_y
= dy
.next_pow2
+ min_y
253 # Inkscape's --export-area inverts the Y axis. It uses the lower left corner of
254 # the drawing area where as queries return coordinates from the top left.
255 var y0
= page_height
- max_y
256 var y1
= page_height
- min_y
258 # Output png file to assets
259 var png_path
= "{assets_path}/images/{drawing_name}.png"
260 var proc2
= new Process.from_a
(prog
, [drawing
, "--without-gui",
261 "--export-dpi={(90.0*scale).to_i}",
262 "--export-png={png_path}",
263 "--export-area={min_x}:{y0}:{max_x}:{y1}",
264 "--export-background=#000000", "--export-background-opacity=0.0"])