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