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 //# OUTPUT FORMATTING\r
51 //########################################################################\r
52 \r
53 \r
54 /**\r
55 * We want to control floating output format\r
56 */\r
57 static JavaFXOutput::String dstr(double d)\r
58 {\r
59 char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
60 g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
61 "%.8f", (gdouble)d);\r
62 JavaFXOutput::String s = dbuf;\r
63 return s;\r
64 }\r
65 \r
66 \r
67 \r
68 \r
69 /**\r
70 * Output data to the buffer, printf()-style\r
71 */\r
72 void JavaFXOutput::out(const char *fmt, ...)\r
73 {\r
74 va_list args;\r
75 va_start(args, fmt);\r
76 gchar *output = g_strdup_vprintf(fmt, args);\r
77 va_end(args);\r
78 outbuf.append(output);\r
79 g_free(output);\r
80 }\r
81 \r
82 \r
83 /**\r
84 * Output the file header\r
85 */\r
86 bool JavaFXOutput::doHeader(const String &name)\r
87 {\r
88 time_t tim = time(NULL);\r
89 out("/*###################################################################\n");\r
90 out("### This JavaFX document was generated by Inkscape\n");\r
91 out("### http://www.inkscape.org\n");\r
92 out("### Created: %s", ctime(&tim));\r
93 out("### Version: %s\n", INKSCAPE_VERSION);\r
94 out("#####################################################################\n");\r
95 out("### NOTES:\n");\r
96 out("### ============\n");\r
97 out("### JavaFX information can be found at\n");\r
98 out("### hhttps://openjfx.dev.java.net\n");\r
99 out("###\n");\r
100 out("### If you have any problems with this output, please see the\n");\r
101 out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
102 out("### the #inkscape channel on irc.freenode.net . \n");\r
103 out("###\n");\r
104 out("###################################################################*/\n");\r
105 out("\n\n");\r
106 out("/*###################################################################\n");\r
107 out("## Exports in this file\n");\r
108 out("##==========================\n");\r
109 out("## Shapes : %d\n", nrShapes);\r
110 out("## Segments : %d\n", nrSegments);\r
111 out("## Nodes : %d\n", nrNodes);\r
112 out("###################################################################*/\n");\r
113 out("\n\n");\r
114 out("import javafx.ui.UIElement;\n");\r
115 out("import javafx.ui.*;\n");\r
116 out("import javafx.ui.canvas.*;\n");\r
117 out("\n");\r
118 out("import java.lang.System;\n");\r
119 out("\n\n");\r
120 out("public class %s extends CompositeNode {\n", name.c_str());\r
121 out("}\n");\r
122 out("\n\n");\r
123 out("function %s.composeNode() =\n", name.c_str());\r
124 out("Group\n");\r
125 out(" {\n");\r
126 out(" content:\n");\r
127 out(" [\n");\r
128 return true;\r
129 }\r
130 \r
131 \r
132 \r
133 /**\r
134 * Output the file footer\r
135 */\r
136 bool JavaFXOutput::doTail(const String &name)\r
137 {\r
138 out(" ] // content\n");\r
139 out(" }; // Group\n");\r
140 out("// end function %s.composeNode()\n", name.c_str());\r
141 out("\n\n\n\n");\r
142 out("Frame {\n");\r
143 out(" title: \"Test\"\n");\r
144 out(" width: 500\n");\r
145 out(" height: 500\n");\r
146 out(" onClose: function()\n");\r
147 out(" {\n");\r
148 out(" return System.exit( 0 );\n");\r
149 out(" }\n");\r
150 out(" visible: true\n");\r
151 out(" content: Canvas {\n");\r
152 out(" content: tux{}\n");\r
153 out(" }\n");\r
154 out("}\n");\r
155 out("/*###################################################################\n");\r
156 out("### E N D C L A S S %s\n", name.c_str());\r
157 out("###################################################################*/\n");\r
158 out("\n\n");\r
159 return true;\r
160 }\r
161 \r
162 \r
163 /**\r
164 * Output the curve data to buffer\r
165 */\r
166 bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
167 {\r
168 using Geom::X;\r
169 using Geom::Y;\r
170 \r
171 Geom::Matrix tf = sp_item_i2d_affine(item);\r
172 \r
173 //### Get the Shape\r
174 if (!SP_IS_SHAPE(item))//Bulia's suggestion. Allow all shapes\r
175 return true;\r
176 \r
177 SPShape *shape = SP_SHAPE(item);\r
178 SPCurve *curve = shape->curve;\r
179 if (curve->is_empty())\r
180 return true;\r
181 \r
182 nrShapes++;\r
183 \r
184 out(" /*###################################################\n");\r
185 out(" ### PATH: %s\n", id.c_str());\r
186 out(" ###################################################*/\n");\r
187 out(" Path \n");\r
188 out(" {\n");\r
189 out(" id: \"%s\"\n", id.c_str());\r
190 \r
191 /**\r
192 * Get the fill and stroke of the shape\r
193 */\r
194 SPStyle *style = SP_OBJECT_STYLE(shape);\r
195 if (style)\r
196 {\r
197 /**\r
198 * Fill\r
199 */\r
200 if (style->fill.isColor())\r
201 {\r
202 // see color.h for how to parse SPColor\r
203 gint alpha = 0xffffffff;\r
204 guint32 rgba = style->fill.value.color.toRGBA32(alpha);\r
205 unsigned int r = SP_RGBA32_R_U(rgba);\r
206 unsigned int g = SP_RGBA32_G_U(rgba);\r
207 unsigned int b = SP_RGBA32_B_U(rgba);\r
208 unsigned int a = SP_RGBA32_A_U(rgba);\r
209 out(" fill: rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)\n",\r
210 r, g, b, a);\r
211 }\r
212 /**\r
213 * Stroke\r
214 */\r
215 /**\r
216 * TODO: stroke code here\r
217 */\r
218 }\r
219 \r
220 \r
221 // convert the path to only lineto's and cubic curveto's:\r
222 Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
223 \r
224 //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
225 guint segmentCount = 0;\r
226 for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
227 segmentCount += (*it).size();\r
228 if (it->closed())\r
229 segmentCount += 1;\r
230 }\r
231 \r
232 out(" d:\n");\r
233 out(" [\n");\r
234 \r
235 unsigned int segmentNr = 0;\r
236 \r
237 nrSegments += segmentCount;\r
238 \r
239 /**\r
240 * For all Subpaths in the <path>\r
241 */ \r
242 for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
243 {\r
244 Geom::Point p = pit->front().initialPoint() * tf;\r
245 out(" MoveTo {\n");\r
246 out(" x: %s\n", dstr(p[X]).c_str());\r
247 out(" y: %s\n", dstr(p[Y]).c_str());\r
248 out(" absolute: true\n");\r
249 out(" },\n");\r
250 \r
251 /**\r
252 * For all segments in the subpath\r
253 */ \r
254 for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
255 {\r
256 //### LINE\r
257 if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||\r
258 dynamic_cast<Geom::HLineSegment const *>(&*cit) ||\r
259 dynamic_cast<Geom::VLineSegment const *>(&*cit) )\r
260 {\r
261 Geom::Point p = cit->initialPoint() * tf;\r
262 out(" LineTo {\n");\r
263 out(" x: %s\n", dstr(p[X]).c_str());\r
264 out(" y: %s\n", dstr(p[Y]).c_str());\r
265 out(" absolute: true\n");\r
266 out(" },\n");\r
267 nrNodes++;\r
268 }\r
269 //### BEZIER\r
270 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))\r
271 {\r
272 std::vector<Geom::Point> points = cubic->points();\r
273 Geom::Point p0 = points[1] * tf;\r
274 Geom::Point p1 = points[2] * tf;\r
275 Geom::Point p2 = points[3] * tf;\r
276 out(" CurveTo {\n");\r
277 out(" x1: %s\n", dstr(p0[X]).c_str());\r
278 out(" y1: %s\n", dstr(p0[Y]).c_str());\r
279 out(" x2: %s\n", dstr(p1[X]).c_str());\r
280 out(" y2: %s\n", dstr(p1[Y]).c_str());\r
281 out(" x3: %s\n", dstr(p2[X]).c_str());\r
282 out(" y3: %s\n", dstr(p2[Y]).c_str());\r
283 out(" absolute: true\n");\r
284 out(" },\n");\r
285 nrNodes++;\r
286 }\r
287 else\r
288 {\r
289 g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
290 }\r
291 segmentNr++;\r
292 }\r
293 if (pit->closed())\r
294 {\r
295 out(" ClosePath {},\n");\r
296 }\r
297 }\r
298 \r
299 out(" ] // d\n");\r
300 out(" }, // Path\n");\r
301 \r
302 \r
303 out(" /*###################################################\n");\r
304 out(" ### end path %s\n", id.c_str());\r
305 out(" ###################################################*/\n\n\n\n");\r
306 \r
307 return true;\r
308 }\r
309 \r
310 \r
311 /**\r
312 * Output the curve data to buffer\r
313 */\r
314 bool JavaFXOutput::doCurvesRecursive(SPDocument *doc, Inkscape::XML::Node *node)\r
315 {\r
316 /**\r
317 * If the object is an Item, try processing it\r
318 */ \r
319 char *str = (char *) node->attribute("id");\r
320 SPObject *reprobj = doc->getObjectByRepr(node);\r
321 if (SP_IS_ITEM(reprobj) && str)\r
322 {\r
323 SPItem *item = SP_ITEM(reprobj);\r
324 String id = str;\r
325 if (!doCurve(item, id))\r
326 return false;\r
327 }\r
328 \r
329 /**\r
330 * Descend into children\r
331 */ \r
332 for (Inkscape::XML::Node *child = node->firstChild() ; child ;\r
333 child = child->next())\r
334 {\r
335 if (!doCurvesRecursive(doc, child))\r
336 return false;\r
337 }\r
338 \r
339 return true;\r
340 }\r
341 \r
342 \r
343 /**\r
344 * Output the curve data to buffer\r
345 */\r
346 bool JavaFXOutput::doCurves(SPDocument *doc)\r
347 {\r
348 \r
349 double bignum = 1000000.0;\r
350 minx = bignum;\r
351 maxx = -bignum;\r
352 miny = bignum;\r
353 maxy = -bignum;\r
354 \r
355 if (!doCurvesRecursive(doc, doc->rroot))\r
356 return false;\r
357 \r
358 return true;\r
359 \r
360 }\r
361 \r
362 \r
363 \r
364 \r
365 //########################################################################\r
366 //# M A I N O U T P U T\r
367 //########################################################################\r
368 \r
369 \r
370 \r
371 /**\r
372 * Set values back to initial state\r
373 */\r
374 void JavaFXOutput::reset()\r
375 {\r
376 nrNodes = 0;\r
377 nrSegments = 0;\r
378 nrShapes = 0;\r
379 outbuf.clear();\r
380 }\r
381 \r
382 \r
383 \r
384 /**\r
385 * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
386 */\r
387 bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)\r
388 {\r
389 reset();\r
390 \r
391 \r
392 String name = Glib::path_get_basename(uri);\r
393 int pos = name.find('.');\r
394 if (pos > 0)\r
395 name = name.substr(0, pos);\r
396 \r
397 \r
398 //###### SAVE IN POV FORMAT TO BUFFER\r
399 //# Lets do the curves first, to get the stats\r
400 \r
401 if (!doCurves(doc))\r
402 return false;\r
403 String curveBuf = outbuf;\r
404 outbuf.clear();\r
405 \r
406 if (!doHeader(name))\r
407 return false;\r
408 \r
409 outbuf.append(curveBuf);\r
410 \r
411 if (!doTail(name))\r
412 return false;\r
413 \r
414 \r
415 \r
416 \r
417 //###### WRITE TO FILE\r
418 FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");\r
419 if (!f)\r
420 {\r
421 g_warning("Could open JavaFX file '%s' for writing", uri);\r
422 return false;\r
423 }\r
424 \r
425 for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
426 {\r
427 fputc(*iter, f);\r
428 }\r
429 \r
430 fclose(f);\r
431 \r
432 return true;\r
433 }\r
434 \r
435 \r
436 \r
437 \r
438 //########################################################################\r
439 //# EXTENSION API\r
440 //########################################################################\r
441 \r
442 \r
443 \r
444 #include "clear-n_.h"\r
445 \r
446 \r
447 \r
448 /**\r
449 * API call to save document\r
450 */\r
451 void\r
452 JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
453 SPDocument *doc, gchar const *uri)\r
454 {\r
455 if (!saveDocument(doc, uri))\r
456 {\r
457 g_warning("Could not save JavaFX file '%s'", uri);\r
458 }\r
459 }\r
460 \r
461 \r
462 \r
463 /**\r
464 * Make sure that we are in the database\r
465 */\r
466 bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
467 {\r
468 /* We don't need a Key\r
469 if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
470 return FALSE;\r
471 */\r
472 \r
473 return true;\r
474 }\r
475 \r
476 \r
477 \r
478 /**\r
479 * This is the definition of JavaFX output. This function just\r
480 * calls the extension system with the memory allocated XML that\r
481 * describes the data.\r
482 */\r
483 void\r
484 JavaFXOutput::init()\r
485 {\r
486 Inkscape::Extension::build_from_mem(\r
487 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
488 "<name>" N_("JavaFX Output") "</name>\n"\r
489 "<id>org.inkscape.output.jfx</id>\n"\r
490 "<output>\n"\r
491 "<extension>.fx</extension>\n"\r
492 "<mimetype>text/x-javafx-script</mimetype>\n"\r
493 "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
494 "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
495 "</output>\n"\r
496 "</inkscape-extension>",\r
497 new JavaFXOutput());\r
498 }\r
499 \r
500 \r
501 \r
502 \r
503 \r
504 } // namespace Internal\r
505 } // namespace Extension\r
506 } // namespace Inkscape\r
507 \r
508 \r
509 /*\r
510 Local Variables:\r
511 mode:c++\r
512 c-file-style:"stroustrup"\r
513 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
514 indent-tabs-mode:nil\r
515 fill-column:99\r
516 End:\r
517 */\r
518 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r