Code

merge silveiro's and my changes
[inkscape.git] / src / extension / internal / javafx-out.cpp
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  *   Silveira Neto <silveiraneto@gmail.com>\r
10  *   Jim Clarke <Jim.Clarke@sun.com>\r
11  *\r
12  * Copyright (C) 2008 Authors\r
13  *\r
14  * Released under GNU GPL, read the file 'COPYING' for more information\r
15  */\r
16 \r
17 \r
18 #ifdef HAVE_CONFIG_H\r
19 # include <config.h>\r
20 #endif\r
21 #include "javafx-out.h"\r
22 #include <inkscape.h>\r
23 #include <inkscape_version.h>\r
24 #include <sp-path.h>\r
25 #include <sp-linear-gradient.h>\r
26 #include <sp-radial-gradient.h>\r
27 #include <style.h>\r
28 #include <display/curve.h>\r
29 #include <display/canvas-bpath.h>\r
30 #include <svg/svg.h>\r
31 #include <extension/system.h>\r
32 #include <2geom/pathvector.h>\r
33 #include <2geom/rect.h>\r
34 #include <2geom/bezier-curve.h>\r
35 #include <2geom/hvlinesegment.h>\r
36 #include "helper/geom.h"\r
37 #include "helper/geom-curves.h"\r
38 #include <io/sys.h>\r
39 \r
40 \r
41 #include <string>\r
42 #include <stdio.h>\r
43 #include <stdarg.h>\r
44 \r
45 \r
46 namespace Inkscape\r
47 {\r
48 namespace Extension\r
49 {\r
50 namespace Internal\r
51 {\r
52 \r
53 \r
54 \r
55 \r
56 //########################################################################\r
57 //# M E S S A G E S\r
58 //########################################################################\r
59 \r
60 static void err(const char *fmt, ...)\r
61 {\r
62     va_list args;\r
63     va_start(args, fmt);\r
64     g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);\r
65     va_end(args);\r
66 }\r
67 \r
68 \r
69 //########################################################################\r
70 //# U T I L I T Y\r
71 //########################################################################\r
72 \r
73 /**\r
74  * Got this method from Bulia, and modified it a bit.  It basically\r
75  * starts with this style, gets its SPObject parent, walks up the object\r
76  * tree and finds all of the opacities and multiplies them.\r
77  *\r
78  * We use this for our "flat" object output.  If the code is modified\r
79  * to reflect a tree of <groups>, then this will be unneccessary.\r
80  */\r
81 static double effective_opacity(const SPStyle *style)\r
82 {\r
83     double val = 1.0;\r
84     for (SPObject const *obj = style->object; obj ; obj = obj->parent)\r
85         {\r
86         style = SP_OBJECT_STYLE(obj);\r
87         if (style)\r
88             val *= SP_SCALE24_TO_FLOAT(style->opacity.value);\r
89         }\r
90     return val;\r
91 }\r
92 \r
93 //########################################################################\r
94 //# OUTPUT FORMATTING\r
95 //########################################################################\r
96 \r
97 \r
98 /**\r
99  * We want to control floating output format\r
100  */\r
101 static JavaFXOutput::String dstr(double d)\r
102 {\r
103     char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
104     g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
105                   "%.8f", (gdouble)d);\r
106     JavaFXOutput::String s = dbuf;\r
107     return s;\r
108 }\r
109 \r
110 #define DSTR(d) (dstr(d).c_str())\r
111 \r
112 static JavaFXOutput::String istr(double d)\r
113 {\r
114     char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
115     g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
116                   "%.0f", (gdouble)d);\r
117     JavaFXOutput::String s = dbuf;\r
118     return s;\r
119 }\r
120 \r
121 #define ISTR(d) (istr(d).c_str())\r
122 \r
123 /**\r
124  * Format an rgba() string\r
125  */\r
126 static JavaFXOutput::String rgba(guint32 rgba)\r
127 {\r
128     unsigned int r = SP_RGBA32_R_U(rgba);\r
129     unsigned int g = SP_RGBA32_G_U(rgba);\r
130     unsigned int b = SP_RGBA32_B_U(rgba);\r
131     unsigned int a = SP_RGBA32_A_U(rgba);\r
132     char buf[80];\r
133     snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",\r
134                            r, g, b, DSTR((double)a/256.0));\r
135     JavaFXOutput::String s = buf;\r
136     return s;\r
137 }\r
138 \r
139 \r
140 /**\r
141  * Format an rgba() string for a color and a 0.0-1.0 alpha\r
142  */\r
143 static JavaFXOutput::String rgba(SPColor color, gdouble alpha)\r
144 {\r
145     return rgba(color.toRGBA32(alpha));\r
146 }\r
147 \r
148 /**\r
149  * Map Inkscape linecap styles to JavaFX\r
150  */\r
151 static JavaFXOutput::String getStrokeLineCap(unsigned value) {\r
152     switch(value) {\r
153         case SP_STROKE_LINECAP_BUTT:\r
154             return "StrokeLineCap.BUTT";\r
155         case SP_STROKE_LINECAP_ROUND:\r
156             return "StrokeLineCap.ROUND";\r
157         case SP_STROKE_LINECAP_SQUARE:\r
158             return "StrokeLineCap.SQUARE";\r
159         default:\r
160             return "INVALID LINE CAP";\r
161     }\r
162 }\r
163 \r
164 \r
165 /**\r
166  * Map Inkscape linejoin styles to JavaFX\r
167  */\r
168 static JavaFXOutput::String getStrokeLineJoin(unsigned value) {\r
169     switch(value) {\r
170         case SP_STROKE_LINEJOIN_MITER:\r
171             return "StrokeLineJoin.MITER";\r
172         case SP_STROKE_LINEJOIN_ROUND:\r
173             return "StrokeLineJoin.ROUND";\r
174         case SP_STROKE_LINEJOIN_BEVEL:\r
175             return "StrokeLineJoin.BEVEL";\r
176         default:\r
177             return "INVALID LINE JOIN";\r
178     }\r
179 }\r
180 \r
181 \r
182 \r
183 /**\r
184  *  Output data to the buffer, printf()-style\r
185  */\r
186 void JavaFXOutput::out(const char *fmt, ...)\r
187 {\r
188     va_list args;\r
189     va_start(args, fmt);\r
190     gchar *output = g_strdup_vprintf(fmt, args);\r
191     va_end(args);\r
192     outbuf.append(output);\r
193     g_free(output);\r
194 }\r
195 \r
196 \r
197 \r
198 /**\r
199  * Output the file header\r
200  */\r
201 bool JavaFXOutput::doHeader()\r
202 {\r
203     time_t tim = time(NULL);\r
204     out("/*###################################################################\n");\r
205     out("### This JavaFX document was generated by Inkscape\n");\r
206     out("### http://www.inkscape.org\n");\r
207     out("### Created: %s", ctime(&tim));\r
208     out("### Version: %s\n", INKSCAPE_VERSION);\r
209     out("#####################################################################\n");\r
210     out("### NOTES:\n");\r
211     out("### ============\n");\r
212     out("### JavaFX information can be found at\n");\r
213     out("### hhttps://openjfx.dev.java.net\n");\r
214     out("###\n");\r
215     out("### If you have any problems with this output, please see the\n");\r
216     out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
217     out("### the #inkscape channel on irc.freenode.net . \n");\r
218     out("###\n");\r
219     out("###################################################################*/\n");\r
220     out("\n\n");\r
221     out("/*###################################################################\n");\r
222     out("##   Exports in this file\n");\r
223     out("##==========================\n");\r
224     out("##    Shapes   : %d\n", nrShapes);\r
225     out("##    Nodes    : %d\n", nrNodes);\r
226     out("###################################################################*/\n");\r
227     out("\n\n");\r
228 \r
229     // import javafx libraries we can need\r
230     out("import javafx.application.*;\n");\r
231     out("import javafx.scene.*;\n");\r
232     out("import javafx.scene.geometry.*;\n");\r
233     out("import javafx.scene.transform.*;\n");\r
234     out("import javafx.scene.paint.*;\n");\r
235     out("\n");\r
236 \r
237     out("\n\n");\r
238 \r
239     // Creates a class extended from CustomNode\r
240     out("public class %s extends CustomNode {\n", name.c_str());\r
241 \r
242     return true;\r
243 }\r
244 \r
245 \r
246 \r
247 /**\r
248  *  Output the file footer\r
249  */\r
250 bool JavaFXOutput::doTail()\r
251 {\r
252     float border = 25.0;\r
253 \r
254     // Write the tail of CustomNode\r
255     out("           ] // content\n");\r
256     out("           transform: Translate { x : %s, y : %s }\n",\r
257                   DSTR((-minx) + border), DSTR((-miny) + border) );\r
258     out("       } // Group\n");\r
259     out("   } // function create()\n");\r
260     out("} // class %s\n", name.c_str());\r
261     out("\n");\r
262 \r
263     // Frame\r
264     out("Frame {\n");\r
265     out("    title: \"%s\"\n", name.c_str());\r
266     out("    width: %s\n", ISTR(maxx-minx + border * 2.0));\r
267     out("    height: %s\n", ISTR(maxy-miny + border * 2.0));\r
268     out("    visible: true\n");\r
269 \r
270     // Stage\r
271     out("    stage: Stage {\n");\r
272     out("        content: %s{}\n", name.c_str());\r
273     out("    } // Stage\n");\r
274 \r
275     out("} // Frame\n");\r
276 \r
277     out("\n");\r
278 \r
279     out("/*###################################################################\n");\r
280     out("### E N D   C L A S S    %s\n", name.c_str());\r
281     out("###################################################################*/\n");\r
282     out("\n\n");\r
283     return true;\r
284 }\r
285 \r
286 \r
287 \r
288 /**\r
289  *  Output gradient information to the buffer\r
290  */\r
291 bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)\r
292 {\r
293     if (SP_IS_LINEARGRADIENT(grad))\r
294         {\r
295         SPLinearGradient *g = SP_LINEARGRADIENT(grad);\r
296         out("    /* create LinearGradient for %s */\n", id.c_str());\r
297         out("    private function %s(): LinearGradient {\n",  id.c_str());\r
298         out("        LinearGradient {\n");\r
299         std::vector<SPGradientStop> stops = g->vector.stops;\r
300         if (stops.size() > 0)\r
301             {\r
302             out("            stops:\n");\r
303             out("                [\n");\r
304             for (unsigned int i = 0 ; i<stops.size() ; i++)\r
305                 {\r
306                 SPGradientStop stop = stops[i];\r
307                 out("                Stop {\n");\r
308                 out("                    offset: %s\n", DSTR(stop.offset));\r
309                 out("                    color: %s\n", rgba(stop.color, stop.opacity).c_str());\r
310                 out("                },\n");\r
311                 }\r
312             out("            ]\n");\r
313             }\r
314         out("        };\n");\r
315         out("    } // end LinearGradient: %s\n", id.c_str());\r
316         out("\n\n");\r
317         }\r
318     else if (SP_IS_RADIALGRADIENT(grad))\r
319         {\r
320         SPRadialGradient *g = SP_RADIALGRADIENT(grad);\r
321         out("    /* create RadialGradient for %s */\n", id.c_str());\r
322         out("    private function %s() {\n", id.c_str());\r
323         out("        RadialGradient {\n");\r
324         out("            centerX: %s\n", DSTR(g->cx.value));\r
325         out("            centerY: %s\n", DSTR(g->cy.value));\r
326         out("            focusX: %s\n", DSTR(g->fx.value));\r
327         out("            focusY: %s\n", DSTR(g->fy.value));\r
328         out("            radius: %s\n", DSTR(g->r.value));\r
329         std::vector<SPGradientStop> stops = g->vector.stops;\r
330         if (stops.size() > 0)\r
331             {\r
332             out("            stops:\n");\r
333             out("            [\n");\r
334             for (unsigned int i = 0 ; i<stops.size() ; i++)\r
335                 {\r
336                 SPGradientStop stop = stops[i];\r
337                 out("                Stop {\n");\r
338                 out("                    offset: %s\n", DSTR(stop.offset));\r
339                 out("                    color: %s\n", rgba(stop.color, stop.opacity).c_str());\r
340                 out("                },\n");\r
341                 }\r
342             out("            ]\n");\r
343             }\r
344         out("        };\n");\r
345         out("    } // end RadialGradient: %s\n", id.c_str());\r
346         out("\n\n");\r
347         }\r
348     else\r
349         {\r
350         err("Unknown gradient type for '%s'\n", id.c_str());\r
351         return false;\r
352         }\r
353 \r
354 \r
355     return true;\r
356 }\r
357 \r
358 \r
359 \r
360 \r
361 /**\r
362  *  Output an element's style attribute\r
363  */\r
364 bool JavaFXOutput::doStyle(SPStyle *style)\r
365 {\r
366     if (!style)\r
367         return true;\r
368 \r
369     out("            opacity: %s\n", DSTR(effective_opacity(style)));\r
370 \r
371     /**\r
372      * Fill\r
373      */\r
374     SPIPaint fill = style->fill;\r
375     if (fill.isColor())\r
376         {\r
377         // see color.h for how to parse SPColor\r
378         out("            fill: %s\n",\r
379             rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());\r
380         }\r
381     else if (fill.isPaintserver()){\r
382         if (fill.value.href && fill.value.href->getURI() ){\r
383             String uri = fill.value.href->getURI()->toString();\r
384             /* trim the anchor '#' from the front */\r
385             if (uri.size() > 0 && uri[0]=='#')\r
386                 uri = uri.substr(1);\r
387             out("            fill: %s()\n", uri.c_str());\r
388         }\r
389     }\r
390 \r
391 \r
392     /**\r
393      * Stroke\r
394      */\r
395     /**\r
396      *NOTE:  Things in style we can use:\r
397      * SPIPaint stroke;\r
398      * SPILength stroke_width;\r
399      * SPIEnum stroke_linecap;\r
400      * SPIEnum stroke_linejoin;\r
401      * SPIFloat stroke_miterlimit;\r
402      * NRVpathDash stroke_dash;\r
403      * unsigned stroke_dasharray_set : 1;\r
404      * unsigned stroke_dasharray_inherit : 1;\r
405      * unsigned stroke_dashoffset_set : 1;\r
406      * SPIScale24 stroke_opacity;\r
407      */\r
408     if (style->stroke_opacity.value > 0)\r
409         {\r
410         SPIPaint stroke = style->stroke;\r
411         out("            stroke: %s\n",\r
412             rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());\r
413         double strokewidth = style->stroke_width.value;\r
414         unsigned linecap = style->stroke_linecap.value;\r
415         unsigned linejoin = style->stroke_linejoin.value;\r
416         out("            strokeWidth: %s\n", DSTR(strokewidth));\r
417         out("            strokeLineCap: %s\n", getStrokeLineCap(linecap).c_str());\r
418         out("            strokeLineJoin: %s\n", getStrokeLineJoin(linejoin).c_str());\r
419         out("            strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));\r
420         if(style->stroke_dasharray_set) {\r
421            if(style->stroke_dashoffset_set) {\r
422                out("            strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));\r
423            }\r
424            out("            strokeDashArray: [ ");\r
425            for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {\r
426                if(i > 0) {\r
427                    out(", %.2lf", style->stroke_dash.dash[i]);\r
428                }else {\r
429                    out(" %.2lf", style->stroke_dash.dash[i]);\r
430                }\r
431            }\r
432            out(" ]\n");\r
433         }\r
434 \r
435         }\r
436 \r
437     return true;\r
438 }\r
439 \r
440 \r
441 #if 1\r
442 \r
443 /**\r
444  *  Output the curve data to buffer\r
445  */\r
446 bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
447 {\r
448     using Geom::X;\r
449     using Geom::Y;\r
450 \r
451     //### Get the Shape\r
452     if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes\r
453         return true;\r
454 \r
455     SPShape *shape = SP_SHAPE(item);\r
456     SPCurve *curve = shape->curve;\r
457     if (curve->is_empty())\r
458         return true;\r
459 \r
460     nrShapes++;\r
461 \r
462     out("    /** path %s */\n", id.c_str());\r
463     out("    private function %s() : Path {\n",id.c_str());\r
464     out("        Path {\n");\r
465     out("            id: \"%s\"\n", id.c_str());\r
466 \r
467     /**\r
468      * Output the style information\r
469      */\r
470     if (!doStyle(SP_OBJECT_STYLE(shape)))\r
471         return false;\r
472 \r
473     // convert the path to only lineto's and cubic curveto's:\r
474     Geom::Scale yflip(1.0, -1.0);\r
475     Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
476     Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
477 \r
478     //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
479     guint segmentCount = 0;\r
480     for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
481         segmentCount += (*it).size();\r
482         if (it->closed())\r
483             segmentCount += 1;\r
484     }\r
485 \r
486     out("            elements: [\n");\r
487 \r
488     unsigned int segmentNr = 0;\r
489 \r
490     nrNodes += segmentCount;\r
491 \r
492     Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );\r
493 \r
494     /**\r
495      * For all Subpaths in the <path>\r
496      */\r
497     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
498         {\r
499         Geom::Point p = pit->front().initialPoint();\r
500         cminmax.expandTo(p);\r
501         out("                MoveTo {\n");\r
502         out("                    x: %s\n", DSTR(p[X]));\r
503         out("                    y: %s\n", DSTR(p[Y]));\r
504         out("                },\n");\r
505 \r
506         /**\r
507          * For all segments in the subpath\r
508          */\r
509         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
510             {\r
511             //### LINE\r
512             if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||\r
513                 dynamic_cast<Geom::HLineSegment const *>(&*cit) ||\r
514                 dynamic_cast<Geom::VLineSegment const *>(&*cit) )\r
515                 {\r
516                 Geom::Point p = cit->finalPoint();\r
517                 out("                LineTo {\n");\r
518                 out("                    x: %s\n", DSTR(p[X]));\r
519                 out("                    y: %s\n", DSTR(p[Y]));\r
520                 out("                },\n");\r
521                 nrNodes++;\r
522                 }\r
523             //### BEZIER\r
524             else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))\r
525                 {\r
526                 std::vector<Geom::Point> points = cubic->points();\r
527                 Geom::Point p1 = points[1];\r
528                 Geom::Point p2 = points[2];\r
529                 Geom::Point p3 = points[3];\r
530                 out("                CurveTo {\n");\r
531                 out("                    controlX1: %s\n", DSTR(p1[X]));\r
532                 out("                    controlY1: %s\n", DSTR(p1[Y]));\r
533                 out("                    controlX2: %s\n", DSTR(p2[X]));\r
534                 out("                    controlY2: %s\n", DSTR(p2[Y]));\r
535                 out("                    x: %s\n", DSTR(p3[X]));\r
536                 out("                    y: %s\n", DSTR(p3[Y]));\r
537                 out("                },\n");\r
538                 nrNodes++;\r
539                 }\r
540             else\r
541                 {\r
542                 g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
543                 }\r
544             segmentNr++;\r
545             cminmax.expandTo(cit->finalPoint());\r
546             }\r
547         if (pit->closed())\r
548             {\r
549             out("                ClosePath {},\n");\r
550             }\r
551         }\r
552 \r
553     out("            ] // elements\n");\r
554     out("        }; // Path\n");\r
555     out("    } // end path %s\n\n", id.c_str());\r
556 \r
557     double cminx = cminmax.min()[X];\r
558     double cmaxx = cminmax.max()[X];\r
559     double cminy = cminmax.min()[Y];\r
560     double cmaxy = cminmax.max()[Y];\r
561 \r
562     if (cminx < minx)\r
563         minx = cminx;\r
564     if (cmaxx > maxx)\r
565         maxx = cmaxx;\r
566     if (cminy < miny)\r
567         miny = cminy;\r
568     if (cmaxy > maxy)\r
569         maxy = cmaxy;\r
570 \r
571     return true;\r
572 }\r
573 \r
574 \r
575 \r
576 #else\r
577 \r
578 /**\r
579  *  Output the curve data to buffer\r
580  */\r
581 bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
582 {\r
583     using Geom::X;\r
584     using Geom::Y;\r
585 \r
586     //### Get the Shape\r
587     if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes\r
588         return true;\r
589 \r
590     SPShape *shape = SP_SHAPE(item);\r
591     SPCurve *curve = shape->curve;\r
592     if (curve->is_empty())\r
593         return true;\r
594 \r
595     nrShapes++;\r
596 \r
597     out("        SVGPath \n");\r
598     out("        {\n");\r
599     out("        id: \"%s\"\n", id.c_str());\r
600 \r
601     /**\r
602      * Output the style information\r
603      */\r
604     if (!doStyle(SP_OBJECT_STYLE(shape)))\r
605         return false;\r
606 \r
607     // convert the path to only lineto's and cubic curveto's:\r
608     Geom::Scale yflip(1.0, -1.0);\r
609     Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
610     Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
611     \r
612     //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
613     nrNodes = 0;\r
614     for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
615         nrNodes += (*it).size();\r
616         if (it->closed())\r
617             nrNodes += 1;\r
618     }\r
619 \r
620     char *dataStr = sp_svg_write_path(pathv);\r
621     out("        content: \"%s\"\n", dataStr);\r
622     free(dataStr);\r
623 \r
624     Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); \r
625 \r
626     /**\r
627      * Get the Min and Max X and Y extends for the Path. \r
628      * ....For all Subpaths in the <path>\r
629      */      \r
630     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
631         {\r
632         cminmax.expandTo(pit->front().initialPoint());\r
633         /**\r
634          * For all segments in the subpath\r
635          */                      \r
636         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
637             {\r
638             cminmax.expandTo(cit->finalPoint());\r
639             }\r
640         }\r
641 \r
642     out("        },\n");\r
643 \r
644     double cminx = cminmax.min()[X];\r
645     double cmaxx = cminmax.max()[X];\r
646     double cminy = cminmax.min()[Y];\r
647     double cmaxy = cminmax.max()[Y];\r
648 \r
649     if (cminx < minx)\r
650         minx = cminx;\r
651     if (cmaxx > maxx)\r
652         maxx = cmaxx;\r
653     if (cminy < miny)\r
654         miny = cminy;\r
655     if (cmaxy > maxy)\r
656         maxy = cmaxy;\r
657 \r
658     return true;\r
659 }\r
660 \r
661 \r
662 \r
663 #endif  /* #if o */\r
664 \r
665 \r
666 \r
667 /**\r
668  *  Output the tree data to buffer\r
669  */\r
670 bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)\r
671 {\r
672     /**\r
673      * Check the type of node and process\r
674      */\r
675     String id;\r
676     if (!obj->id)\r
677         {\r
678         char buf[16];\r
679         sprintf(buf, "id%d", idindex++);\r
680         id = buf;\r
681         }\r
682     else\r
683         {\r
684         id = obj->id;\r
685         }\r
686     if (SP_IS_ITEM(obj))\r
687         {\r
688         SPItem *item = SP_ITEM(obj);\r
689         if (!doCurve(item, id))\r
690             return false;\r
691         }\r
692     else if (SP_IS_GRADIENT(obj))\r
693         {\r
694         SPGradient *grad = SP_GRADIENT(obj);\r
695         if (!doGradient(grad, id))\r
696             return false;\r
697         }\r
698 \r
699     /**\r
700      * Descend into children\r
701      */      \r
702     for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
703         {\r
704                 if (!doTreeRecursive(doc, child))\r
705                     return false;\r
706                 }\r
707 \r
708     return true;\r
709 }\r
710 \r
711 \r
712 /**\r
713  *  Output the curve data to buffer\r
714  */\r
715 bool JavaFXOutput::doTree(SPDocument *doc)\r
716 {\r
717 \r
718     double bignum = 1000000.0;\r
719     minx  =  bignum;\r
720     maxx  = -bignum;\r
721     miny  =  bignum;\r
722     maxy  = -bignum;\r
723 \r
724     if (!doTreeRecursive(doc, doc->root))\r
725         return false;\r
726 \r
727     return true;\r
728 \r
729 }\r
730 \r
731 \r
732 bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj) {\r
733     /**\r
734      * Check the type of node and process\r
735      */\r
736     String id;\r
737     if (!obj->id)\r
738         {\r
739         char buf[16];\r
740         sprintf(buf, "id%d", idindex++);\r
741         id = buf;\r
742         }\r
743     else\r
744         {\r
745         id = obj->id;\r
746         }\r
747 \r
748     if (SP_IS_ITEM(obj)) {\r
749         SPItem *item = SP_ITEM(obj);\r
750         //### Get the Shape\r
751         if (SP_IS_SHAPE(item)) {//Bulia's suggestion.  Allow all shapes\r
752             SPShape *shape = SP_SHAPE(item);\r
753             SPCurve *curve = shape->curve;\r
754             if (!curve->is_empty())\r
755                 out("               %s(),\n", id.c_str());\r
756         }\r
757     }\r
758     else if (SP_IS_GRADIENT(obj)) {\r
759         //TODO: what to do with Gradient within body?????\r
760         //SPGradient *grad = SP_GRADIENT(reprobj);\r
761         //if (!doGradient(grad, id))\r
762         //    return false;\r
763     }\r
764 \r
765     /**\r
766      * Descend into children\r
767      */\r
768     for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
769         {\r
770                 if (!doBody(doc, child))\r
771                     return false;\r
772                 }\r
773 \r
774     return true;\r
775 }\r
776 \r
777 \r
778 \r
779 //########################################################################\r
780 //# M A I N    O U T P U T\r
781 //########################################################################\r
782 \r
783 \r
784 \r
785 /**\r
786  *  Set values back to initial state\r
787  */\r
788 void JavaFXOutput::reset()\r
789 {\r
790     nrNodes    = 0;\r
791     nrShapes   = 0;\r
792     idindex    = 0;\r
793     name.clear();\r
794     outbuf.clear();\r
795     foutbuf.clear();\r
796 }\r
797 \r
798 \r
799 \r
800 /**\r
801  * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
802  */\r
803 bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)\r
804 {\r
805     reset();\r
806 \r
807 \r
808     name = Glib::path_get_basename(uri);\r
809     int pos = name.find('.');\r
810     if (pos > 0)\r
811         name = name.substr(0, pos);\r
812 \r
813 \r
814     //###### SAVE IN JAVAFX FORMAT TO BUFFER\r
815     //# Lets do the curves first, to get the stats\r
816 \r
817     if (!doTree(doc))\r
818         return false;\r
819     String curveBuf = outbuf;\r
820     outbuf.clear();\r
821 \r
822     if (!doHeader())\r
823         return false;\r
824 \r
825     outbuf.append(curveBuf);\r
826 \r
827 #ifdef JAVAFX_SDK_1_0\r
828     out("   override function create(): Node {\n");\r
829 #else\r
830     out("   public function create(): Node {\n");\r
831 #endif\r
832     out("       Group {\n");\r
833     out("           content: [\n");\r
834     idindex    = 0;\r
835 \r
836     doBody(doc, doc->root);\r
837 \r
838     if (!doTail())\r
839         return false;\r
840 \r
841 \r
842 \r
843     //###### WRITE TO FILE\r
844     FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");\r
845     if (!f)\r
846         {\r
847         err("Could open JavaFX file '%s' for writing", uri);\r
848         return false;\r
849         }\r
850 \r
851     for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
852         {\r
853         fputc(*iter, f);\r
854         }\r
855         \r
856     fclose(f);\r
857     \r
858     return true;\r
859 }\r
860 \r
861 \r
862 \r
863 \r
864 //########################################################################\r
865 //# EXTENSION API\r
866 //########################################################################\r
867 \r
868 \r
869 \r
870 #include "clear-n_.h"\r
871 \r
872 \r
873 \r
874 /**\r
875  * API call to save document\r
876 */\r
877 void\r
878 JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
879                         SPDocument *doc, gchar const *uri)\r
880 {\r
881     if (!saveDocument(doc, uri))\r
882         {\r
883         g_warning("Could not save JavaFX file '%s'", uri);\r
884         }\r
885 }\r
886 \r
887 \r
888 \r
889 /**\r
890  * Make sure that we are in the database\r
891  */\r
892 bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
893 {\r
894     /* We don't need a Key\r
895     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
896         return FALSE;\r
897     */\r
898 \r
899     return true;\r
900 }\r
901 \r
902 \r
903 \r
904 /**\r
905  * This is the definition of JavaFX output.  This function just\r
906  * calls the extension system with the memory allocated XML that\r
907  * describes the data.\r
908 */\r
909 void\r
910 JavaFXOutput::init()\r
911 {\r
912     Inkscape::Extension::build_from_mem(\r
913         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
914             "<name>" N_("JavaFX Output") "</name>\n"\r
915             "<id>org.inkscape.output.jfx</id>\n"\r
916             "<output>\n"\r
917                 "<extension>.fx</extension>\n"\r
918                 "<mimetype>text/x-javafx-script</mimetype>\n"\r
919                 "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
920                 "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
921             "</output>\n"\r
922         "</inkscape-extension>",\r
923         new JavaFXOutput());\r
924 }\r
925 \r
926 \r
927 \r
928 \r
929 \r
930 }  // namespace Internal\r
931 }  // namespace Extension\r
932 }  // namespace Inkscape\r
933 \r
934 \r
935 /*\r
936   Local Variables:\r
937   mode:c++\r
938   c-file-style:"stroustrup"\r
939   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
940   indent-tabs-mode:nil\r
941   fill-column:99\r
942   End:\r
943 */\r
944 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r