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