Code

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