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