1 /*\r
2 * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.\r
3 *\r
4 * For information on the JavaFX file format, see:\r
5 * https://openjfx.dev.java.net/\r
6 *\r
7 * Authors:\r
8 * Bob Jamison <ishmal@inkscape.org>\r
9 *\r
10 * Copyright (C) 2008 Authors\r
11 *\r
12 * Released under GNU GPL, read the file 'COPYING' for more information\r
13 */\r
14 \r
15 \r
16 #ifdef HAVE_CONFIG_H\r
17 # include <config.h>\r
18 #endif\r
19 #include "javafx-out.h"\r
20 #include <inkscape.h>\r
21 #include <inkscape_version.h>\r
22 #include <sp-path.h>\r
23 #include <style.h>\r
24 #include <display/curve.h>\r
25 #include <libnr/n-art-bpath.h>\r
26 #include <extension/system.h>\r
27 #include <2geom/pathvector.h>\r
28 #include <2geom/rect.h>\r
29 #include <2geom/bezier-curve.h>\r
30 #include <2geom/hvlinesegment.h>\r
31 #include "helper/geom.h"\r
32 #include <io/sys.h>\r
33 \r
34 #include <string>\r
35 #include <stdio.h>\r
36 #include <stdarg.h>\r
37 \r
38 \r
39 namespace Inkscape\r
40 {\r
41 namespace Extension\r
42 {\r
43 namespace Internal\r
44 {\r
45 \r
46 \r
47 \r
48 \r
49 //########################################################################\r
50 //# U T I L I T Y\r
51 //########################################################################\r
52 \r
53 \r
54 \r
55 /**\r
56 * This function searches the Repr tree recursively from the given node,\r
57 * and adds refs to all nodes with the given name, to the result vector\r
58 */\r
59 static void\r
60 findElementsByTagName(std::vector<Inkscape::XML::Node *> &results,\r
61 Inkscape::XML::Node *node,\r
62 char const *name)\r
63 {\r
64 if ( !name || strcmp(node->name(), name) == 0 )\r
65 results.push_back(node);\r
66 \r
67 for (Inkscape::XML::Node *child = node->firstChild() ; child ;\r
68 child = child->next())\r
69 findElementsByTagName( results, child, name );\r
70 \r
71 }\r
72 \r
73 \r
74 \r
75 //########################################################################\r
76 //# OUTPUT FORMATTING\r
77 //########################################################################\r
78 \r
79 \r
80 /**\r
81 * We want to control floating output format\r
82 */\r
83 static JavaFXOutput::String dstr(double d)\r
84 {\r
85 char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
86 g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
87 "%.8f", (gdouble)d);\r
88 JavaFXOutput::String s = dbuf;\r
89 return s;\r
90 }\r
91 \r
92 \r
93 \r
94 \r
95 /**\r
96 * Output data to the buffer, printf()-style\r
97 */\r
98 void JavaFXOutput::out(const char *fmt, ...)\r
99 {\r
100 va_list args;\r
101 va_start(args, fmt);\r
102 gchar *output = g_strdup_vprintf(fmt, args);\r
103 va_end(args);\r
104 outbuf.append(output);\r
105 g_free(output);\r
106 }\r
107 \r
108 \r
109 /**\r
110 * Output the file header\r
111 */\r
112 bool JavaFXOutput::doHeader(gchar const *uri)\r
113 {\r
114 time_t tim = time(NULL);\r
115 out("/*###################################################################\n");\r
116 out("### This JavaFX document was generated by Inkscape\n");\r
117 out("### http://www.inkscape.org\n");\r
118 out("### Created: %s", ctime(&tim));\r
119 out("### Version: %s\n", INKSCAPE_VERSION);\r
120 out("#####################################################################\n");\r
121 out("### NOTES:\n");\r
122 out("### ============\n");\r
123 out("### JavaFX information can be found at\n");\r
124 out("### hhttps://openjfx.dev.java.net\n");\r
125 out("###\n");\r
126 out("### If you have any problems with this output, please see the\n");\r
127 out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
128 out("### the #inkscape channel on irc.freenode.net . \n");\r
129 out("###\n");\r
130 out("###################################################################*/\n");\r
131 out("\n\n");\r
132 out("/*###################################################################\n");\r
133 out("## Exports in this file\n");\r
134 out("##==========================\n");\r
135 out("## Shapes : %d\n", nrShapes);\r
136 out("## Segments : %d\n", nrSegments);\r
137 out("## Nodes : %d\n", nrNodes);\r
138 out("###################################################################*/\n");\r
139 out("\n\n");\r
140 out("import javafx.ui.UIElement;\n");\r
141 out("import javafx.ui.*;\n");\r
142 out("import javafx.ui.canvas.*;\n");\r
143 out("\n");\r
144 out("public class %s extends CompositeNode {\n", uri);\r
145 out("\n");\r
146 out("\n\n\n");\r
147 return true;\r
148 }\r
149 \r
150 \r
151 \r
152 /**\r
153 * Output the file footer\r
154 */\r
155 bool JavaFXOutput::doTail(gchar const *uri)\r
156 {\r
157 out("} // end class $s\n", uri);\r
158 out("\n\n");\r
159 out("/*###################################################################\n");\r
160 out("### E N D F I L E\n");\r
161 out("###################################################################*/\n");\r
162 out("\n\n");\r
163 return true;\r
164 }\r
165 \r
166 \r
167 \r
168 /**\r
169 * Output the curve data to buffer\r
170 */\r
171 bool JavaFXOutput::doCurves(SPDocument *doc, gchar const *uri)\r
172 {\r
173 using Geom::X;\r
174 using Geom::Y;\r
175 \r
176 \r
177 std::vector<Inkscape::XML::Node *>results;\r
178 findElementsByTagName(results, doc->rroot, NULL);\r
179 //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);\r
180 if (results.size() == 0)\r
181 return true;\r
182 \r
183 out("function composeNode()\n");\r
184 out("{\n");\r
185 out("Group\n");\r
186 out(" {\n");\r
187 out(" content:\n");\r
188 out(" [\n");\r
189 \r
190 for (unsigned int indx = 0; indx < results.size() ; indx++)\r
191 {\r
192 //### Fetch the object from the repr info\r
193 Inkscape::XML::Node *rpath = results[indx];\r
194 char *str = (char *) rpath->attribute("id");\r
195 if (!str)\r
196 continue;\r
197 \r
198 String id = str;\r
199 SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);\r
200 if (!reprobj)\r
201 continue;\r
202 \r
203 //### Get the transform of the item\r
204 if (!SP_IS_ITEM(reprobj))\r
205 continue;\r
206 \r
207 SPItem *item = SP_ITEM(reprobj);\r
208 Geom::Matrix tf = sp_item_i2d_affine(item);\r
209 \r
210 //### Get the Shape\r
211 if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion. Allow all shapes\r
212 continue;\r
213 \r
214 SPShape *shape = SP_SHAPE(reprobj);\r
215 SPCurve *curve = shape->curve;\r
216 if (curve->is_empty())\r
217 continue;\r
218 \r
219 nrShapes++;\r
220 \r
221 out(" /*###################################################\n");\r
222 out(" ### PATH: %s\n", id.c_str());\r
223 out(" ###################################################*/\n");\r
224 out(" Path \n");\r
225 out(" {\n");\r
226 out(" name : \"%s\"\n", id.c_str());\r
227 \r
228 //Try to get the fill color of the shape\r
229 SPStyle *style = SP_OBJECT_STYLE(shape);\r
230 /* fixme: Handle other fill types, even if this means translating gradients to a single\r
231 flat colour. */\r
232 if (style && (style->fill.isColor()))\r
233 {\r
234 // see color.h for how to parse SPColor\r
235 gint alpha = 0xffffffff;\r
236 guint32 rgba = style->fill.value.color.toRGBA32(alpha);\r
237 unsigned int r = SP_RGBA32_R_U(rgba);\r
238 unsigned int g = SP_RGBA32_G_U(rgba);\r
239 unsigned int b = SP_RGBA32_B_U(rgba);\r
240 unsigned int a = SP_RGBA32_A_U(rgba);\r
241 out(" fill: rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)\n",\r
242 r, g, b, a);\r
243 }\r
244 \r
245 \r
246 // convert the path to only lineto's and cubic curveto's:\r
247 Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
248 \r
249 //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
250 guint segmentCount = 0;\r
251 for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
252 segmentCount += (*it).size();\r
253 if (it->closed())\r
254 segmentCount += 1;\r
255 }\r
256 \r
257 out(" d :\n");\r
258 out(" [\n");\r
259 \r
260 int segmentNr = 0;\r
261 \r
262 nrSegments += segmentCount;\r
263 \r
264 for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {\r
265 \r
266 \r
267 for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {\r
268 \r
269 //### LINE\r
270 if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||\r
271 dynamic_cast<Geom::HLineSegment const *>(&*cit) ||\r
272 dynamic_cast<Geom::VLineSegment const *>(&*cit) )\r
273 {\r
274 out(" LineTo {\n");\r
275 out(" x: %s\n", dstr(cit->initialPoint()[X]).c_str());\r
276 out(" y: %s\n", dstr(cit->initialPoint()[Y]).c_str());\r
277 out(" absolute: true\n");\r
278 out(" }");\r
279 nrNodes++;\r
280 }\r
281 //### BEZIER\r
282 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {\r
283 std::vector<Geom::Point> points = cubic->points();\r
284 out(" CurveTo {\n");\r
285 out(" x1: %s\n", dstr(points[0][X]).c_str());\r
286 out(" y1: %s\n", dstr(points[0][Y]).c_str());\r
287 out(" x2: %s\n", dstr(points[1][X]).c_str());\r
288 out(" y2: %s\n", dstr(points[1][Y]).c_str());\r
289 out(" x3: %s\n", dstr(points[2][X]).c_str());\r
290 out(" y3: %s\n", dstr(points[3][Y]).c_str());\r
291 out(" smooth: false\n");\r
292 out(" absolute: true\n");\r
293 out(" }");\r
294 nrNodes++;\r
295 }\r
296 else {\r
297 g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
298 }\r
299 \r
300 if (segmentNr <= segmentCount)\r
301 out(",\n");\r
302 else\r
303 out("\n");\r
304 \r
305 }\r
306 }\r
307 \r
308 out(" ] // d\n");\r
309 out(" } // Path\n");\r
310 \r
311 \r
312 out(" /*###################################################\n");\r
313 out(" ### end path %s\n", id.c_str());\r
314 out(" ###################################################*/\n\n\n\n");\r
315 \r
316 }//for\r
317 \r
318 out(" ] // content\n");\r
319 out(" } // Group\n");\r
320 out("} // function composeNode()\n");\r
321 \r
322 return true;\r
323 \r
324 }\r
325 \r
326 \r
327 \r
328 \r
329 //########################################################################\r
330 //# M A I N O U T P U T\r
331 //########################################################################\r
332 \r
333 \r
334 \r
335 /**\r
336 * Set values back to initial state\r
337 */\r
338 void JavaFXOutput::reset()\r
339 {\r
340 nrNodes = 0;\r
341 nrSegments = 0;\r
342 nrShapes = 0;\r
343 outbuf.clear();\r
344 }\r
345 \r
346 \r
347 \r
348 /**\r
349 * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
350 */\r
351 bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)\r
352 {\r
353 reset();\r
354 \r
355 //###### SAVE IN POV FORMAT TO BUFFER\r
356 //# Lets do the curves first, to get the stats\r
357 if (!doCurves(doc, uri))\r
358 return false;\r
359 String curveBuf = outbuf;\r
360 outbuf.clear();\r
361 \r
362 if (!doHeader(uri))\r
363 return false;\r
364 \r
365 outbuf.append(curveBuf);\r
366 \r
367 if (!doTail(uri))\r
368 return false;\r
369 \r
370 \r
371 \r
372 \r
373 //###### WRITE TO FILE\r
374 FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");\r
375 if (!f)\r
376 {\r
377 g_warning("Could open JavaFX file '%s' for writing", uri);\r
378 return false;\r
379 }\r
380 \r
381 for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
382 {\r
383 fputc(*iter, f);\r
384 }\r
385 \r
386 fclose(f);\r
387 \r
388 return true;\r
389 }\r
390 \r
391 \r
392 \r
393 \r
394 //########################################################################\r
395 //# EXTENSION API\r
396 //########################################################################\r
397 \r
398 \r
399 \r
400 #include "clear-n_.h"\r
401 \r
402 \r
403 \r
404 /**\r
405 * API call to save document\r
406 */\r
407 void\r
408 JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
409 SPDocument *doc, gchar const *uri)\r
410 {\r
411 if (!saveDocument(doc, uri))\r
412 {\r
413 g_warning("Could not save JavaFX file '%s'", uri);\r
414 }\r
415 }\r
416 \r
417 \r
418 \r
419 /**\r
420 * Make sure that we are in the database\r
421 */\r
422 bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
423 {\r
424 /* We don't need a Key\r
425 if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
426 return FALSE;\r
427 */\r
428 \r
429 return true;\r
430 }\r
431 \r
432 \r
433 \r
434 /**\r
435 * This is the definition of JavaFX output. This function just\r
436 * calls the extension system with the memory allocated XML that\r
437 * describes the data.\r
438 */\r
439 void\r
440 JavaFXOutput::init()\r
441 {\r
442 Inkscape::Extension::build_from_mem(\r
443 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
444 "<name>" N_("JavaFX Output") "</name>\n"\r
445 "<id>org.inkscape.output.jfx</id>\n"\r
446 "<output>\n"\r
447 "<extension>.fx</extension>\n"\r
448 "<mimetype>text/x-javafx-script</mimetype>\n"\r
449 "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
450 "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
451 "</output>\n"\r
452 "</inkscape-extension>",\r
453 new JavaFXOutput());\r
454 }\r
455 \r
456 \r
457 \r
458 \r
459 \r
460 } // namespace Internal\r
461 } // namespace Extension\r
462 } // namespace Inkscape\r
463 \r
464 \r
465 /*\r
466 Local Variables:\r
467 mode:c++\r
468 c-file-style:"stroustrup"\r
469 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
470 indent-tabs-mode:nil\r
471 fill-column:99\r
472 End:\r
473 */\r
474 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r