Code

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